From 64cd25a817f64a832b3bac07d1b610241fc775b8 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 17:03:06 +0300 Subject: [PATCH 01/53] feat(cores): cpu_range set --- crates/core-manager/src/cpu_range.rs | 214 +++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 crates/core-manager/src/cpu_range.rs diff --git a/crates/core-manager/src/cpu_range.rs b/crates/core-manager/src/cpu_range.rs new file mode 100644 index 0000000000..92a96cd818 --- /dev/null +++ b/crates/core-manager/src/cpu_range.rs @@ -0,0 +1,214 @@ +use core_affinity::CoreId; +use range_set_blaze::RangeSetBlaze; +use std::str::FromStr; +use thiserror::Error; + +#[derive(Debug)] +pub struct CpuRange(RangeSetBlaze); +impl CpuRange { + pub fn contains(&self, value: CoreId) -> bool { + self.0.contains(value.id) + } +} + +impl TryFrom> for CpuRange { + type Error = Error; + + fn try_from(range: RangeSetBlaze) -> Result { + if range.is_empty() { + return Err(Error::EmptyRange); + } + let available_cores = num_cpus::get(); + let range_max = range.last().unwrap(); //last always exists, can't be empty + if range_max > available_cores { + return Err(Error::RangeIsTooBig { available_cores }); + } + + Ok(CpuRange(range)) + } +} + +#[derive(Debug, Error, PartialEq)] +pub enum Error { + #[error("Range can't be an empty")] + EmptyRange, + #[error("Range is too big. Available cores: {available_cores}")] + RangeIsTooBig { available_cores: usize }, +} + +#[derive(Debug, Error, PartialEq)] +pub enum ParseError { + #[error("Range can't be an empty")] + EmptyRange, + #[error("Failed to build a cpu range: {err}")] + CpuRangeError { + #[source] + err: Error, + }, + #[error("Failed to parse str: {raw_str}")] + WrongRangeFormat { raw_str: String }, +} +impl FromStr for CpuRange { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + let mut result: RangeSetBlaze = RangeSetBlaze::new(); + let trimmed = s.trim(); + if trimmed.is_empty() { + return Err(ParseError::EmptyRange); + } + + for part in trimmed.split(',') { + let trimmed = part.trim(); + let split: Vec<&str> = trimmed.split('-').collect(); + match split[..] { + [l, r] => { + let l = l + .parse::() + .map_err(|_| ParseError::WrongRangeFormat { + raw_str: trimmed.to_string(), + })?; + let r = r + .parse::() + .map_err(|_| ParseError::WrongRangeFormat { + raw_str: trimmed.to_string(), + })?; + result.ranges_insert(l..=r); + } + [value] => { + let value = + value + .parse::() + .map_err(|_| ParseError::WrongRangeFormat { + raw_str: trimmed.to_string(), + })?; + result.insert(value); + } + _ => { + return Err(ParseError::WrongRangeFormat { + raw_str: trimmed.to_string(), + }) + } + } + } + + CpuRange::try_from(result).map_err(|err| ParseError::CpuRangeError { err }) + } +} + +#[cfg(test)] +mod tests { + use crate::cpu_range::{CpuRange, Error, ParseError}; + use core_affinity::CoreId; + use range_set_blaze::RangeSetBlaze; + + #[test] + fn range_parsing_test() { + let total_cpus = num_cpus::get(); + let raw_str = format!("0-{}", total_cpus - 1); + let cpu_range: CpuRange = raw_str.parse().unwrap(); + for value in 0..total_cpus { + assert!(cpu_range.contains(CoreId { id: value })); + } + } + + #[test] + fn values_parsing_test() { + let total_cpus = num_cpus::get(); + let raw_str = format!("0,{}", total_cpus - 1); + let cpu_range: CpuRange = raw_str.parse().unwrap(); + assert!(cpu_range.contains(CoreId { id: 0 })); + if total_cpus > 1 { + assert!(cpu_range.contains(CoreId { id: total_cpus - 1 })); + } + } + + #[test] + fn wrong_parsing_test() { + let result = "aaaa".parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!( + err, + ParseError::WrongRangeFormat { + raw_str: "aaaa".to_string() + } + ); + } + } + #[test] + fn wrong_parsing_test_2() { + let result = "1-a".parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!( + err, + ParseError::WrongRangeFormat { + raw_str: "1-a".to_string() + } + ); + } + } + #[test] + fn wrong_parsing_test_3() { + let result = "a-1".parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!( + err, + ParseError::WrongRangeFormat { + raw_str: "a-1".to_string() + } + ); + } + } + + #[test] + fn wrong_parsing_test_4() { + let result = "a-1-2,3".parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!( + err, + ParseError::WrongRangeFormat { + raw_str: "a-1-2".to_string() + } + ); + } + } + + #[test] + fn empty_parsing_test_3() { + let result = "".parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!(err, ParseError::EmptyRange); + } + } + + #[test] + fn big_cpu_count() { + let available_cores = num_cpus::get(); + let str = format!("0-{}", available_cores + 1); + let result = str.parse::(); + assert!(result.is_err()); + if let Err(err) = result { + assert_eq!( + err, + ParseError::CpuRangeError { + err: Error::RangeIsTooBig { available_cores } + } + ); + } + } + + #[test] + fn empty_range_check() { + let range: RangeSetBlaze = RangeSetBlaze::new(); + let cpu_range = CpuRange::try_from(range); + assert!(cpu_range.is_err()); + if let Err(err) = cpu_range { + assert_eq!(err, Error::EmptyRange) + } + } +} From 3db09d256f6afb8b684ab7247c0908613ae6192b Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 19:45:01 +0300 Subject: [PATCH 02/53] feat(cores): core manager --- .../src/{cpu_range.rs => core_set.rs} | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) rename crates/core-manager/src/{cpu_range.rs => core_set.rs} (76%) diff --git a/crates/core-manager/src/cpu_range.rs b/crates/core-manager/src/core_set.rs similarity index 76% rename from crates/core-manager/src/cpu_range.rs rename to crates/core-manager/src/core_set.rs index 92a96cd818..65ee88f65a 100644 --- a/crates/core-manager/src/cpu_range.rs +++ b/crates/core-manager/src/core_set.rs @@ -4,14 +4,14 @@ use std::str::FromStr; use thiserror::Error; #[derive(Debug)] -pub struct CpuRange(RangeSetBlaze); -impl CpuRange { - pub fn contains(&self, value: CoreId) -> bool { - self.0.contains(value.id) +pub struct CoreSet(pub(crate) RangeSetBlaze); +impl CoreSet { + pub fn to_vec(&self) -> Vec { + self.0.iter().map(|v| CoreId { id: v }).collect() } } -impl TryFrom> for CpuRange { +impl TryFrom> for CoreSet { type Error = Error; fn try_from(range: RangeSetBlaze) -> Result { @@ -24,7 +24,25 @@ impl TryFrom> for CpuRange { return Err(Error::RangeIsTooBig { available_cores }); } - Ok(CpuRange(range)) + Ok(CoreSet(range)) + } +} + +impl TryFrom<&[usize]> for CoreSet { + type Error = Error; + + fn try_from(values: &[usize]) -> Result { + if values.is_empty() { + return Err(Error::EmptyRange); + } + let range = RangeSetBlaze::from_iter(values.iter()); + let available_cores = num_cpus::get(); + let range_max = range.last().unwrap(); //last always exists, can't be empty + if range_max > available_cores { + return Err(Error::RangeIsTooBig { available_cores }); + } + + Ok(CoreSet(range)) } } @@ -48,7 +66,7 @@ pub enum ParseError { #[error("Failed to parse str: {raw_str}")] WrongRangeFormat { raw_str: String }, } -impl FromStr for CpuRange { +impl FromStr for CoreSet { type Err = ParseError; fn from_str(s: &str) -> Result { @@ -92,23 +110,22 @@ impl FromStr for CpuRange { } } - CpuRange::try_from(result).map_err(|err| ParseError::CpuRangeError { err }) + CoreSet::try_from(result).map_err(|err| ParseError::CpuRangeError { err }) } } #[cfg(test)] mod tests { - use crate::cpu_range::{CpuRange, Error, ParseError}; - use core_affinity::CoreId; + use crate::core_set::{CoreSet, Error, ParseError}; use range_set_blaze::RangeSetBlaze; #[test] fn range_parsing_test() { let total_cpus = num_cpus::get(); let raw_str = format!("0-{}", total_cpus - 1); - let cpu_range: CpuRange = raw_str.parse().unwrap(); + let cpu_range: CoreSet = raw_str.parse().unwrap(); for value in 0..total_cpus { - assert!(cpu_range.contains(CoreId { id: value })); + assert!(cpu_range.0.contains(value)); } } @@ -116,16 +133,16 @@ mod tests { fn values_parsing_test() { let total_cpus = num_cpus::get(); let raw_str = format!("0,{}", total_cpus - 1); - let cpu_range: CpuRange = raw_str.parse().unwrap(); - assert!(cpu_range.contains(CoreId { id: 0 })); + let cpu_range: CoreSet = raw_str.parse().unwrap(); + assert!(cpu_range.0.contains(0)); if total_cpus > 1 { - assert!(cpu_range.contains(CoreId { id: total_cpus - 1 })); + assert!(cpu_range.0.contains(total_cpus - 1)); } } #[test] fn wrong_parsing_test() { - let result = "aaaa".parse::(); + let result = "aaaa".parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!( @@ -138,7 +155,7 @@ mod tests { } #[test] fn wrong_parsing_test_2() { - let result = "1-a".parse::(); + let result = "1-a".parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!( @@ -151,7 +168,7 @@ mod tests { } #[test] fn wrong_parsing_test_3() { - let result = "a-1".parse::(); + let result = "a-1".parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!( @@ -165,7 +182,7 @@ mod tests { #[test] fn wrong_parsing_test_4() { - let result = "a-1-2,3".parse::(); + let result = "a-1-2,3".parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!( @@ -179,7 +196,7 @@ mod tests { #[test] fn empty_parsing_test_3() { - let result = "".parse::(); + let result = "".parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!(err, ParseError::EmptyRange); @@ -190,7 +207,7 @@ mod tests { fn big_cpu_count() { let available_cores = num_cpus::get(); let str = format!("0-{}", available_cores + 1); - let result = str.parse::(); + let result = str.parse::(); assert!(result.is_err()); if let Err(err) = result { assert_eq!( @@ -205,7 +222,7 @@ mod tests { #[test] fn empty_range_check() { let range: RangeSetBlaze = RangeSetBlaze::new(); - let cpu_range = CpuRange::try_from(range); + let cpu_range = CoreSet::try_from(range); assert!(cpu_range.is_err()); if let Err(err) = cpu_range { assert_eq!(err, Error::EmptyRange) From 5cb2d4fc7f90ea8438eb169ed05ba700e69a6225 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 19:51:45 +0300 Subject: [PATCH 03/53] small improvements --- crates/core-manager/src/core_set.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index 65ee88f65a..8698ff138a 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -7,7 +7,7 @@ use thiserror::Error; pub struct CoreSet(pub(crate) RangeSetBlaze); impl CoreSet { pub fn to_vec(&self) -> Vec { - self.0.iter().map(|v| CoreId { id: v }).collect() + self.0.iter().map(|id| CoreId { id }).collect() } } @@ -32,17 +32,8 @@ impl TryFrom<&[usize]> for CoreSet { type Error = Error; fn try_from(values: &[usize]) -> Result { - if values.is_empty() { - return Err(Error::EmptyRange); - } - let range = RangeSetBlaze::from_iter(values.iter()); - let available_cores = num_cpus::get(); - let range_max = range.last().unwrap(); //last always exists, can't be empty - if range_max > available_cores { - return Err(Error::RangeIsTooBig { available_cores }); - } - - Ok(CoreSet(range)) + let set = RangeSetBlaze::from_iter(values.iter()); + CoreSet::try_from(set) } } From 38ad1cc7e1ed099ed08835aac1109a42f867bbd5 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 20:33:27 +0300 Subject: [PATCH 04/53] small improvements --- crates/core-manager/src/core_set.rs | 202 +++------------------------- 1 file changed, 22 insertions(+), 180 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index 8698ff138a..daf7bd918f 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -1,6 +1,6 @@ +use crate::core_range::CoreRange; use core_affinity::CoreId; use range_set_blaze::RangeSetBlaze; -use std::str::FromStr; use thiserror::Error; #[derive(Debug)] @@ -11,29 +11,20 @@ impl CoreSet { } } -impl TryFrom> for CoreSet { +impl TryFrom for CoreSet { type Error = Error; - fn try_from(range: RangeSetBlaze) -> Result { - if range.is_empty() { - return Err(Error::EmptyRange); - } + fn try_from(range: CoreRange) -> Result { let available_cores = num_cpus::get(); - let range_max = range.last().unwrap(); //last always exists, can't be empty + let range_max = range.last(); if range_max > available_cores { - return Err(Error::RangeIsTooBig { available_cores }); + return Err(Error::RangeIsTooBig { + available_cores, + range, + }); } - Ok(CoreSet(range)) - } -} - -impl TryFrom<&[usize]> for CoreSet { - type Error = Error; - - fn try_from(values: &[usize]) -> Result { - let set = RangeSetBlaze::from_iter(values.iter()); - CoreSet::try_from(set) + Ok(CoreSet(range.0)) } } @@ -41,182 +32,33 @@ impl TryFrom<&[usize]> for CoreSet { pub enum Error { #[error("Range can't be an empty")] EmptyRange, - #[error("Range is too big. Available cores: {available_cores}")] - RangeIsTooBig { available_cores: usize }, -} - -#[derive(Debug, Error, PartialEq)] -pub enum ParseError { - #[error("Range can't be an empty")] - EmptyRange, - #[error("Failed to build a cpu range: {err}")] - CpuRangeError { - #[source] - err: Error, + #[error("Range is too big. Available cores: {available_cores}, requested range: {range:?}")] + RangeIsTooBig { + available_cores: usize, + range: CoreRange, }, - #[error("Failed to parse str: {raw_str}")] - WrongRangeFormat { raw_str: String }, -} -impl FromStr for CoreSet { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - let mut result: RangeSetBlaze = RangeSetBlaze::new(); - let trimmed = s.trim(); - if trimmed.is_empty() { - return Err(ParseError::EmptyRange); - } - - for part in trimmed.split(',') { - let trimmed = part.trim(); - let split: Vec<&str> = trimmed.split('-').collect(); - match split[..] { - [l, r] => { - let l = l - .parse::() - .map_err(|_| ParseError::WrongRangeFormat { - raw_str: trimmed.to_string(), - })?; - let r = r - .parse::() - .map_err(|_| ParseError::WrongRangeFormat { - raw_str: trimmed.to_string(), - })?; - result.ranges_insert(l..=r); - } - [value] => { - let value = - value - .parse::() - .map_err(|_| ParseError::WrongRangeFormat { - raw_str: trimmed.to_string(), - })?; - result.insert(value); - } - _ => { - return Err(ParseError::WrongRangeFormat { - raw_str: trimmed.to_string(), - }) - } - } - } - - CoreSet::try_from(result).map_err(|err| ParseError::CpuRangeError { err }) - } } #[cfg(test)] mod tests { - use crate::core_set::{CoreSet, Error, ParseError}; - use range_set_blaze::RangeSetBlaze; - - #[test] - fn range_parsing_test() { - let total_cpus = num_cpus::get(); - let raw_str = format!("0-{}", total_cpus - 1); - let cpu_range: CoreSet = raw_str.parse().unwrap(); - for value in 0..total_cpus { - assert!(cpu_range.0.contains(value)); - } - } - - #[test] - fn values_parsing_test() { - let total_cpus = num_cpus::get(); - let raw_str = format!("0,{}", total_cpus - 1); - let cpu_range: CoreSet = raw_str.parse().unwrap(); - assert!(cpu_range.0.contains(0)); - if total_cpus > 1 { - assert!(cpu_range.0.contains(total_cpus - 1)); - } - } - - #[test] - fn wrong_parsing_test() { - let result = "aaaa".parse::(); - assert!(result.is_err()); - if let Err(err) = result { - assert_eq!( - err, - ParseError::WrongRangeFormat { - raw_str: "aaaa".to_string() - } - ); - } - } - #[test] - fn wrong_parsing_test_2() { - let result = "1-a".parse::(); - assert!(result.is_err()); - if let Err(err) = result { - assert_eq!( - err, - ParseError::WrongRangeFormat { - raw_str: "1-a".to_string() - } - ); - } - } - #[test] - fn wrong_parsing_test_3() { - let result = "a-1".parse::(); - assert!(result.is_err()); - if let Err(err) = result { - assert_eq!( - err, - ParseError::WrongRangeFormat { - raw_str: "a-1".to_string() - } - ); - } - } - - #[test] - fn wrong_parsing_test_4() { - let result = "a-1-2,3".parse::(); - assert!(result.is_err()); - if let Err(err) = result { - assert_eq!( - err, - ParseError::WrongRangeFormat { - raw_str: "a-1-2".to_string() - } - ); - } - } - - #[test] - fn empty_parsing_test_3() { - let result = "".parse::(); - assert!(result.is_err()); - if let Err(err) = result { - assert_eq!(err, ParseError::EmptyRange); - } - } + use crate::core_range::CoreRange; + use crate::core_set::{CoreSet, Error}; #[test] fn big_cpu_count() { let available_cores = num_cpus::get(); let str = format!("0-{}", available_cores + 1); - let result = str.parse::(); - assert!(result.is_err()); - if let Err(err) = result { + let range = str.parse::().unwrap(); + let core_set = CoreSet::try_from(range.clone()); + assert!(core_set.is_err()); + if let Err(err) = core_set { assert_eq!( err, - ParseError::CpuRangeError { - err: Error::RangeIsTooBig { available_cores } + Error::RangeIsTooBig { + available_cores, + range } ); } } - - #[test] - fn empty_range_check() { - let range: RangeSetBlaze = RangeSetBlaze::new(); - let cpu_range = CoreSet::try_from(range); - assert!(cpu_range.is_err()); - if let Err(err) = cpu_range { - assert_eq!(err, Error::EmptyRange) - } - } } From 79194437401144cd67d11462ec5be1dc5ca31717 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 20:50:46 +0300 Subject: [PATCH 05/53] small improvements --- crates/core-manager/src/core_set.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index daf7bd918f..cc88f2ae80 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -1,15 +1,9 @@ use crate::core_range::CoreRange; -use core_affinity::CoreId; use range_set_blaze::RangeSetBlaze; use thiserror::Error; #[derive(Debug)] pub struct CoreSet(pub(crate) RangeSetBlaze); -impl CoreSet { - pub fn to_vec(&self) -> Vec { - self.0.iter().map(|id| CoreId { id }).collect() - } -} impl TryFrom for CoreSet { type Error = Error; From e49b39e2c125142b9837009e622e1d70cfae5143 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 21:00:58 +0300 Subject: [PATCH 06/53] small improvements --- crates/core-manager/src/core_set.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index cc88f2ae80..563ce4cb37 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -24,8 +24,6 @@ impl TryFrom for CoreSet { #[derive(Debug, Error, PartialEq)] pub enum Error { - #[error("Range can't be an empty")] - EmptyRange, #[error("Range is too big. Available cores: {available_cores}, requested range: {range:?}")] RangeIsTooBig { available_cores: usize, @@ -38,6 +36,21 @@ mod tests { use crate::core_range::CoreRange; use crate::core_set::{CoreSet, Error}; + #[test] + fn simple_set_test() { + let available_cores = num_cpus::get(); + let str = format!("0-{}", available_cores - 1); + let range = str.parse::().unwrap(); + let core_set = CoreSet::try_from(range.clone()); + assert!(core_set.is_ok()); + if let Ok(core_set) = core_set { + assert_eq!( + format!("{:?}", core_set), + format!("CoreSet(0..={})", available_cores - 1) + ) + } + } + #[test] fn big_cpu_count() { let available_cores = num_cpus::get(); @@ -53,6 +66,10 @@ mod tests { range } ); + assert_eq!( + err.to_string(), + "Range is too big. Available cores: 8, requested range: 0..=9" + ); } } } From a4615f8742162193b3f1357c55bdb0a80b992c91 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 21:30:36 +0300 Subject: [PATCH 07/53] small improvements --- crates/core-manager/src/core_set.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index 563ce4cb37..3e610fd0b2 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -2,9 +2,15 @@ use crate::core_range::CoreRange; use range_set_blaze::RangeSetBlaze; use thiserror::Error; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CoreSet(pub(crate) RangeSetBlaze); +impl CoreSet { + pub fn insert(&mut self, value: usize) -> bool { + self.0.insert(value) + } +} + impl TryFrom for CoreSet { type Error = Error; @@ -31,6 +37,13 @@ pub enum Error { }, } +impl Default for CoreSet { + fn default() -> Self { + let cpu_count = num_cpus::get(); + CoreSet(RangeSetBlaze::from_iter(0..cpu_count)) + } +} + #[cfg(test)] mod tests { use crate::core_range::CoreRange; From 521f51c22448a2d748ffb522ea1bd20416f18be2 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 7 Feb 2024 21:47:13 +0300 Subject: [PATCH 08/53] small improvements --- crates/core-manager/src/core_set.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index 3e610fd0b2..67d30cc0fe 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -5,12 +5,6 @@ use thiserror::Error; #[derive(Debug, Clone)] pub struct CoreSet(pub(crate) RangeSetBlaze); -impl CoreSet { - pub fn insert(&mut self, value: usize) -> bool { - self.0.insert(value) - } -} - impl TryFrom for CoreSet { type Error = Error; @@ -48,6 +42,7 @@ impl Default for CoreSet { mod tests { use crate::core_range::CoreRange; use crate::core_set::{CoreSet, Error}; + use std::usize; #[test] fn simple_set_test() { @@ -85,4 +80,12 @@ mod tests { ); } } + + #[test] + fn default_set_is_non_empty() { + let available_cores = num_cpus::get(); + let core_set = CoreSet::default(); + let core_set: Vec = core_set.0.iter().collect(); + assert_eq!(core_set, Vec::from_iter(0..available_cores)); + } } From 511283a5068d477995ab89579cc0cd7d1de18fa3 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 9 Feb 2024 12:27:39 +0300 Subject: [PATCH 09/53] small improvements --- crates/core-manager/src/core_set.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs index 67d30cc0fe..e5852ea3f8 100644 --- a/crates/core-manager/src/core_set.rs +++ b/crates/core-manager/src/core_set.rs @@ -74,10 +74,12 @@ mod tests { range } ); - assert_eq!( - err.to_string(), - "Range is too big. Available cores: 8, requested range: 0..=9" + let expected_str = format!( + "Range is too big. Available cores: {}, requested range: 0..={}", + available_cores, + available_cores + 1 ); + assert_eq!(err.to_string(), expected_str); } } From a305cdc1c379bc6d3308a8aa05a8129ca297e0b1 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 14 Feb 2024 14:05:46 +0300 Subject: [PATCH 10/53] small improvements --- crates/core-manager/src/core_set.rs | 93 ----------------------------- 1 file changed, 93 deletions(-) delete mode 100644 crates/core-manager/src/core_set.rs diff --git a/crates/core-manager/src/core_set.rs b/crates/core-manager/src/core_set.rs deleted file mode 100644 index e5852ea3f8..0000000000 --- a/crates/core-manager/src/core_set.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::core_range::CoreRange; -use range_set_blaze::RangeSetBlaze; -use thiserror::Error; - -#[derive(Debug, Clone)] -pub struct CoreSet(pub(crate) RangeSetBlaze); - -impl TryFrom for CoreSet { - type Error = Error; - - fn try_from(range: CoreRange) -> Result { - let available_cores = num_cpus::get(); - let range_max = range.last(); - if range_max > available_cores { - return Err(Error::RangeIsTooBig { - available_cores, - range, - }); - } - - Ok(CoreSet(range.0)) - } -} - -#[derive(Debug, Error, PartialEq)] -pub enum Error { - #[error("Range is too big. Available cores: {available_cores}, requested range: {range:?}")] - RangeIsTooBig { - available_cores: usize, - range: CoreRange, - }, -} - -impl Default for CoreSet { - fn default() -> Self { - let cpu_count = num_cpus::get(); - CoreSet(RangeSetBlaze::from_iter(0..cpu_count)) - } -} - -#[cfg(test)] -mod tests { - use crate::core_range::CoreRange; - use crate::core_set::{CoreSet, Error}; - use std::usize; - - #[test] - fn simple_set_test() { - let available_cores = num_cpus::get(); - let str = format!("0-{}", available_cores - 1); - let range = str.parse::().unwrap(); - let core_set = CoreSet::try_from(range.clone()); - assert!(core_set.is_ok()); - if let Ok(core_set) = core_set { - assert_eq!( - format!("{:?}", core_set), - format!("CoreSet(0..={})", available_cores - 1) - ) - } - } - - #[test] - fn big_cpu_count() { - let available_cores = num_cpus::get(); - let str = format!("0-{}", available_cores + 1); - let range = str.parse::().unwrap(); - let core_set = CoreSet::try_from(range.clone()); - assert!(core_set.is_err()); - if let Err(err) = core_set { - assert_eq!( - err, - Error::RangeIsTooBig { - available_cores, - range - } - ); - let expected_str = format!( - "Range is too big. Available cores: {}, requested range: 0..={}", - available_cores, - available_cores + 1 - ); - assert_eq!(err.to_string(), expected_str); - } - } - - #[test] - fn default_set_is_non_empty() { - let available_cores = num_cpus::get(); - let core_set = CoreSet::default(); - let core_set: Vec = core_set.0.iter().collect(); - assert_eq!(core_set, Vec::from_iter(0..available_cores)); - } -} From aa762f581cbedf7b91638af173ea81fa25e6a96d Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Feb 2024 16:56:56 +0300 Subject: [PATCH 11/53] pin system threads --- nox/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/nox/Cargo.toml b/nox/Cargo.toml index 3eba1adcb4..0eff0a177e 100644 --- a/nox/Cargo.toml +++ b/nox/Cargo.toml @@ -18,6 +18,7 @@ sorcerer = { workspace = true } health = { workspace = true } core-manager = { workspace = true } dhat = { version = "0.3.2", optional = true } +core_affinity = { git = "https://github.com/fluencelabs/core_affinity_rs/", branch = "master" } serde_json = { workspace = true } fluence-libp2p = { workspace = true } From 2631064240f37e1f2765b3255373f15883089c55 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Feb 2024 23:24:48 +0300 Subject: [PATCH 12/53] pin worker threads --- nox/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nox/Cargo.toml b/nox/Cargo.toml index 0eff0a177e..945628ebf4 100644 --- a/nox/Cargo.toml +++ b/nox/Cargo.toml @@ -18,7 +18,7 @@ sorcerer = { workspace = true } health = { workspace = true } core-manager = { workspace = true } dhat = { version = "0.3.2", optional = true } -core_affinity = { git = "https://github.com/fluencelabs/core_affinity_rs/", branch = "master" } +core_affinity = { workspace = true } serde_json = { workspace = true } fluence-libp2p = { workspace = true } From 0c83ebe80eab07b9a087beecb78f722a618dc02f Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 00:20:44 +0300 Subject: [PATCH 13/53] Fixes --- crates/server-config/src/resolved_config.rs | 2 +- crates/types/src/lib.rs | 1 + crates/types/src/unit_id.rs | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 crates/types/src/unit_id.rs diff --git a/crates/server-config/src/resolved_config.rs b/crates/server-config/src/resolved_config.rs index a2ac112d39..102fffb56c 100644 --- a/crates/server-config/src/resolved_config.rs +++ b/crates/server-config/src/resolved_config.rs @@ -33,7 +33,7 @@ use crate::node_config::{NodeConfig, UnresolvedNodeConfig}; #[derive(Clone, Serialize, Deserialize, Debug)] pub struct UnresolvedConfig { #[serde(flatten)] - dir_config: UnresolvedDirConfig, + pub dir_config: UnresolvedDirConfig, #[serde(flatten)] pub node_config: UnresolvedNodeConfig, diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index d6861bb4c9..f3385cbf38 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -1,2 +1,3 @@ pub mod peer_id; pub mod peer_scope; +pub mod unit_id; diff --git a/crates/types/src/unit_id.rs b/crates/types/src/unit_id.rs new file mode 100644 index 0000000000..bcace340ef --- /dev/null +++ b/crates/types/src/unit_id.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct UnitId(String); + +impl From for UnitId { + fn from(value: String) -> Self { + UnitId(value) + } +} + +impl From<&str> for UnitId { + fn from(value: &str) -> Self { + UnitId(value.to_string()) + } +} From b46d809ac365d016e0a5941a3581d4d3e3c5d277 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 16:11:10 +0300 Subject: [PATCH 14/53] Review fixes --- crates/server-config/src/dir_config.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/server-config/src/dir_config.rs b/crates/server-config/src/dir_config.rs index d3487fda47..13b129cbd4 100644 --- a/crates/server-config/src/dir_config.rs +++ b/crates/server-config/src/dir_config.rs @@ -102,6 +102,16 @@ impl UnresolvedDirConfig { core_state_path, }) } + + pub fn resolve_core_state_path(&self) -> eyre::Result { + let base = to_abs_path(self.base_dir.clone()); + let base = canonicalize(base)?; + let core_state_path = self + .core_state_path + .clone() + .unwrap_or(PathBuf::from("cores_state.toml")); + Ok(base.join(core_state_path)) + } } #[derive(Clone, Debug, Serialize, Deserialize)] From ab2a111f986af2483dbe9f2cbc3f9451d7f6612c Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 16:39:12 +0300 Subject: [PATCH 15/53] Review fixes --- crates/server-config/src/dir_config.rs | 10 ---------- crates/server-config/src/resolved_config.rs | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/crates/server-config/src/dir_config.rs b/crates/server-config/src/dir_config.rs index 13b129cbd4..d3487fda47 100644 --- a/crates/server-config/src/dir_config.rs +++ b/crates/server-config/src/dir_config.rs @@ -102,16 +102,6 @@ impl UnresolvedDirConfig { core_state_path, }) } - - pub fn resolve_core_state_path(&self) -> eyre::Result { - let base = to_abs_path(self.base_dir.clone()); - let base = canonicalize(base)?; - let core_state_path = self - .core_state_path - .clone() - .unwrap_or(PathBuf::from("cores_state.toml")); - Ok(base.join(core_state_path)) - } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/crates/server-config/src/resolved_config.rs b/crates/server-config/src/resolved_config.rs index 102fffb56c..a2ac112d39 100644 --- a/crates/server-config/src/resolved_config.rs +++ b/crates/server-config/src/resolved_config.rs @@ -33,7 +33,7 @@ use crate::node_config::{NodeConfig, UnresolvedNodeConfig}; #[derive(Clone, Serialize, Deserialize, Debug)] pub struct UnresolvedConfig { #[serde(flatten)] - pub dir_config: UnresolvedDirConfig, + dir_config: UnresolvedDirConfig, #[serde(flatten)] pub node_config: UnresolvedNodeConfig, From a0d9c144ada58c394a5572f8968c62ea7dd92917 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 16:42:18 +0300 Subject: [PATCH 16/53] Review fixes --- nox/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nox/src/main.rs b/nox/src/main.rs index 0ea6bc2a67..f93425ae0c 100644 --- a/nox/src/main.rs +++ b/nox/src/main.rs @@ -34,6 +34,7 @@ use tokio::signal; use tokio::sync::oneshot; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; +use core_affinity::{set_mask_for_current, CoreId}; use air_interpreter_fs::write_default_air_interpreter; use aquamarine::{DataStoreConfig, VmConfig}; From 87c162f0abb98ab962340ee412e29f16614e8e47 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 16:42:25 +0300 Subject: [PATCH 17/53] Review fixes --- nox/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nox/src/main.rs b/nox/src/main.rs index f93425ae0c..45b84f6d96 100644 --- a/nox/src/main.rs +++ b/nox/src/main.rs @@ -27,6 +27,7 @@ )] use base64::{engine::general_purpose::STANDARD as base64, Engine}; +use core_affinity::{set_mask_for_current, CoreId}; use eyre::WrapErr; use libp2p::PeerId; use std::sync::Arc; @@ -34,7 +35,6 @@ use tokio::signal; use tokio::sync::oneshot; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; -use core_affinity::{set_mask_for_current, CoreId}; use air_interpreter_fs::write_default_air_interpreter; use aquamarine::{DataStoreConfig, VmConfig}; From ad5b08a9363d986a3c59213b1aa00e012777e9ff Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 16:51:41 +0300 Subject: [PATCH 18/53] Review fixes --- nox/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/nox/Cargo.toml b/nox/Cargo.toml index 945628ebf4..3eba1adcb4 100644 --- a/nox/Cargo.toml +++ b/nox/Cargo.toml @@ -18,7 +18,6 @@ sorcerer = { workspace = true } health = { workspace = true } core-manager = { workspace = true } dhat = { version = "0.3.2", optional = true } -core_affinity = { workspace = true } serde_json = { workspace = true } fluence-libp2p = { workspace = true } From a94f9ea90360993ed8c6e0f3aaea0777903037da Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 17:25:57 +0300 Subject: [PATCH 19/53] Review fixes --- crates/core-manager/src/manager.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index 54def08f0b..a6323ba71d 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -429,6 +429,7 @@ impl CoreManagerFunctions for PersistentCoreManager { drop(lock); let toml = toml::to_string_pretty(&persistent_state) .map_err(|err| PersistError::SerializationError { err })?; + drop(lock); let exists = self.file_path.exists(); let mut file = if exists { File::open(self.file_path.clone()).map_err(|err| PersistError::IoError { err })? From ac292330b32fd82ba4732eb29316a2a42f509306 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 17:38:50 +0300 Subject: [PATCH 20/53] Review fixes --- crates/core-manager/src/manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index a6323ba71d..42c08b6749 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -379,7 +379,7 @@ impl CoreManagerFunctions for PersistentCoreManager { .expect("Unexpected state. Should not be empty never"); for physical_core_id in physical_core_ids { - result_logical_core_ids.insert(physical_core_id); + result_logical_core_ids.insert(physical_core_id.clone()); } } From 2a0f414816a29008b352614def9681769fff465d Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 17 Feb 2024 17:50:45 +0300 Subject: [PATCH 21/53] Review fixes --- crates/core-manager/src/manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index 42c08b6749..a6323ba71d 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -379,7 +379,7 @@ impl CoreManagerFunctions for PersistentCoreManager { .expect("Unexpected state. Should not be empty never"); for physical_core_id in physical_core_ids { - result_logical_core_ids.insert(physical_core_id.clone()); + result_logical_core_ids.insert(physical_core_id); } } From 50bc70735f601d3d988a71e01d3d663bc00b6035 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Feb 2024 19:51:01 +0300 Subject: [PATCH 22/53] use CUID --- crates/types/src/lib.rs | 1 - crates/types/src/unit_id.rs | 16 ---------------- 2 files changed, 17 deletions(-) delete mode 100644 crates/types/src/unit_id.rs diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index f3385cbf38..d6861bb4c9 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -1,3 +1,2 @@ pub mod peer_id; pub mod peer_scope; -pub mod unit_id; diff --git a/crates/types/src/unit_id.rs b/crates/types/src/unit_id.rs deleted file mode 100644 index bcace340ef..0000000000 --- a/crates/types/src/unit_id.rs +++ /dev/null @@ -1,16 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct UnitId(String); - -impl From for UnitId { - fn from(value: String) -> Self { - UnitId(value) - } -} - -impl From<&str> for UnitId { - fn from(value: &str) -> Self { - UnitId(value.to_string()) - } -} From cf56e8053084ebbb02ebc0405fba92c16c04aeb5 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 20 Feb 2024 16:44:55 +0300 Subject: [PATCH 23/53] feat(config): Introduce ephemeral and persistent storage --- crates/core-manager/src/manager.rs | 1 - crates/server-config/src/defaults.rs | 9 +++- crates/server-config/src/dir_config.rs | 69 +++++++++++++++++++++----- nox/src/main.rs | 1 - nox/src/node.rs | 2 +- 5 files changed, 65 insertions(+), 17 deletions(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index a6323ba71d..54def08f0b 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -429,7 +429,6 @@ impl CoreManagerFunctions for PersistentCoreManager { drop(lock); let toml = toml::to_string_pretty(&persistent_state) .map_err(|err| PersistError::SerializationError { err })?; - drop(lock); let exists = self.file_path.exists(); let mut file = if exists { File::open(self.file_path.clone()).map_err(|err| PersistError::IoError { err })? diff --git a/crates/server-config/src/defaults.rs b/crates/server-config/src/defaults.rs index 1c784012e8..4e14cf890b 100644 --- a/crates/server-config/src/defaults.rs +++ b/crates/server-config/src/defaults.rs @@ -99,7 +99,14 @@ pub fn default_base_dir() -> PathBuf { format!(".fluence/v{CONFIG_VERSION}").into() } -pub fn services_base_dir(base_dir: &Path) -> PathBuf { +pub fn persistent_dir(base_dir: &Path) -> PathBuf { + base_dir.join("persistent") +} +pub fn ephemeral_dir(base_dir: &Path) -> PathBuf { + base_dir.join("ephemeral") +} + +pub fn services_dir(base_dir: &Path) -> PathBuf { base_dir.join("services") } diff --git a/crates/server-config/src/dir_config.rs b/crates/server-config/src/dir_config.rs index d3487fda47..f200631b8d 100644 --- a/crates/server-config/src/dir_config.rs +++ b/crates/server-config/src/dir_config.rs @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use crate::defaults::{avm_base_dir, default_base_dir, services_base_dir}; +use crate::defaults::{avm_base_dir, default_base_dir, services_dir}; use air_interpreter_fs::air_interpreter_path; use fs_utils::{canonicalize, create_dirs, to_abs_path}; +use crate::{ephemeral_dir, persistent_dir}; use eyre::WrapErr; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -28,8 +29,17 @@ pub struct UnresolvedDirConfig { #[serde(default = "default_base_dir")] pub base_dir: PathBuf, - /// Base directory for resources needed by application services - pub services_base_dir: Option, + /// Base directory for persistent resources + pub persistent_base_dir: Option, + + /// Base directory for ephemeral resources + pub ephemeral_base_dir: Option, + + /// Base directory for persistent resources needed by application services + pub services_persistent_dir: Option, + + /// Base directory for ephemeral resources needed by application services + pub services_ephemeral_dir: Option, /// Base directory for resources needed by application services pub avm_base_dir: Option, @@ -57,14 +67,34 @@ impl UnresolvedDirConfig { pub fn resolve(self) -> eyre::Result { let base = to_abs_path(self.base_dir); - let services_base_dir = self.services_base_dir.unwrap_or(services_base_dir(&base)); - let avm_base_dir = self.avm_base_dir.unwrap_or(avm_base_dir(&base)); + let ephemeral_base_dir = self.ephemeral_base_dir.unwrap_or(ephemeral_dir(&base)); + let persistent_base_dir = self.persistent_base_dir.unwrap_or(persistent_dir(&base)); + + // ephemeral dirs + let services_ephemeral_dir = self + .services_ephemeral_dir + .unwrap_or(services_dir(&ephemeral_base_dir)); + let avm_base_dir = self + .avm_base_dir + .unwrap_or(avm_base_dir(&ephemeral_base_dir)); + + // persistent dirs + let services_persistent_dir = self + .services_persistent_dir + .unwrap_or(services_dir(&persistent_base_dir)); let air_interpreter_path = self .air_interpreter_path - .unwrap_or(air_interpreter_path(&base)); - let spell_base_dir = self.spell_base_dir.unwrap_or(base.join("spell")); - let keypairs_base_dir = self.keypairs_base_dir.unwrap_or(base.join("keypairs")); - let workers_base_dir = self.workers_base_dir.unwrap_or(base.join("workers")); + .unwrap_or(air_interpreter_path(&persistent_base_dir)); + let spell_base_dir = self + .spell_base_dir + .unwrap_or(persistent_base_dir.join("spell")); + let keypairs_base_dir = self + .keypairs_base_dir + .unwrap_or(persistent_base_dir.join("keypairs")); + let workers_base_dir = self + .workers_base_dir + .unwrap_or(persistent_base_dir.join("workers")); + let cc_events_dir = self.cc_events_dir.unwrap_or(base.join("cc_events")); let core_state_path = self .core_state_path @@ -73,27 +103,39 @@ impl UnresolvedDirConfig { create_dirs(&[ &base, - &services_base_dir, + // ephemeral dirs + &ephemeral_base_dir, + &services_ephemeral_dir, &avm_base_dir, + // persistent dirs + &persistent_base_dir, + &services_persistent_dir, &spell_base_dir, &keypairs_base_dir, &workers_base_dir, + // other &cc_events_dir, ]) .context("creating configured directories")?; let base = canonicalize(base)?; - let services_base_dir = canonicalize(services_base_dir)?; + // ephemeral dirs let avm_base_dir = canonicalize(avm_base_dir)?; + let services_ephemeral_dir = canonicalize(services_ephemeral_dir)?; + + // persistent dirs + let services_persistent_dir = canonicalize(services_persistent_dir)?; let spell_base_dir = canonicalize(spell_base_dir)?; let keypairs_base_dir = canonicalize(keypairs_base_dir)?; let workers_base_dir = canonicalize(workers_base_dir)?; + let cc_events_dir = canonicalize(cc_events_dir)?; Ok(ResolvedDirConfig { base_dir: base, - services_base_dir, avm_base_dir, + services_ephemeral_dir, + services_persistent_dir, air_interpreter_path, spell_base_dir, keypairs_base_dir, @@ -107,9 +149,10 @@ impl UnresolvedDirConfig { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ResolvedDirConfig { pub base_dir: PathBuf, - pub services_base_dir: PathBuf, /// Directory where particle's prev_data is stored pub avm_base_dir: PathBuf, + pub services_ephemeral_dir: PathBuf, + pub services_persistent_dir: PathBuf, /// Directory where interpreter's WASM module is stored pub air_interpreter_path: PathBuf, pub spell_base_dir: PathBuf, diff --git a/nox/src/main.rs b/nox/src/main.rs index 45b84f6d96..0ea6bc2a67 100644 --- a/nox/src/main.rs +++ b/nox/src/main.rs @@ -27,7 +27,6 @@ )] use base64::{engine::general_purpose::STANDARD as base64, Engine}; -use core_affinity::{set_mask_for_current, CoreId}; use eyre::WrapErr; use libp2p::PeerId; use std::sync::Arc; diff --git a/nox/src/node.rs b/nox/src/node.rs index 039164c623..71a2792ac3 100644 --- a/nox/src/node.rs +++ b/nox/src/node.rs @@ -147,7 +147,7 @@ impl Node { let services_config = ServicesConfig::new( scopes.get_host_peer_id(), - config.dir_config.services_base_dir.clone(), + config.dir_config.services_persistent_dir.clone(), config_utils::particles_vault_dir(&config.dir_config.avm_base_dir), config.services_envs.clone(), config.management_peer_id, From 76707d38f8e0f2b6760b47189f85cd746c780f83 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 20 Feb 2024 23:41:00 +0300 Subject: [PATCH 24/53] fix --- crates/created-swarm/src/swarm.rs | 7 +++---- crates/server-config/src/dir_config.rs | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/created-swarm/src/swarm.rs b/crates/created-swarm/src/swarm.rs index dad449f852..93d5b6cf27 100644 --- a/crates/created-swarm/src/swarm.rs +++ b/crates/created-swarm/src/swarm.rs @@ -39,9 +39,7 @@ use futures::future::BoxFuture; use futures::stream::iter; use nox::{Connectivity, Node}; use particle_protocol::ProtocolConfig; -use server_config::{ - system_services_config, BootstrapConfig, ChainListenerConfig, UnresolvedConfig, -}; +use server_config::{system_services_config, BootstrapConfig, ChainListenerConfig, UnresolvedConfig, persistent_dir}; use tempfile::TempDir; use test_constants::{EXECUTION_TIMEOUT, TRANSPORT_TIMEOUT}; use tokio::sync::oneshot; @@ -306,7 +304,8 @@ pub fn aqua_vm_config( peer_id, tmp_dir, .. } = vm_config; - let air_interpreter = air_interpreter_path(&tmp_dir); + let persistent_dir = persistent_dir(&tmp_dir); + let air_interpreter = air_interpreter_path(&persistent_dir ); write_default_air_interpreter(&air_interpreter).expect("write air interpreter"); VmConfig::new(peer_id, air_interpreter, None) diff --git a/crates/server-config/src/dir_config.rs b/crates/server-config/src/dir_config.rs index f200631b8d..f5e7747201 100644 --- a/crates/server-config/src/dir_config.rs +++ b/crates/server-config/src/dir_config.rs @@ -95,11 +95,13 @@ impl UnresolvedDirConfig { .workers_base_dir .unwrap_or(persistent_base_dir.join("workers")); - let cc_events_dir = self.cc_events_dir.unwrap_or(base.join("cc_events")); + let cc_events_dir = self + .cc_events_dir + .unwrap_or(persistent_base_dir.join("cc_events")); let core_state_path = self .core_state_path .clone() - .unwrap_or(base.join("cores_state.toml")); + .unwrap_or(persistent_base_dir.join("cores_state.toml")); create_dirs(&[ &base, From 92ed7e7cf75de77203d49a6a0c13f2c58b7ba164 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 00:25:28 +0300 Subject: [PATCH 25/53] fix --- crates/created-swarm/src/swarm.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/created-swarm/src/swarm.rs b/crates/created-swarm/src/swarm.rs index 93d5b6cf27..ea5f3e9c1e 100644 --- a/crates/created-swarm/src/swarm.rs +++ b/crates/created-swarm/src/swarm.rs @@ -39,7 +39,9 @@ use futures::future::BoxFuture; use futures::stream::iter; use nox::{Connectivity, Node}; use particle_protocol::ProtocolConfig; -use server_config::{system_services_config, BootstrapConfig, ChainListenerConfig, UnresolvedConfig, persistent_dir}; +use server_config::{ + persistent_dir, system_services_config, BootstrapConfig, ChainListenerConfig, UnresolvedConfig, +}; use tempfile::TempDir; use test_constants::{EXECUTION_TIMEOUT, TRANSPORT_TIMEOUT}; use tokio::sync::oneshot; @@ -50,6 +52,7 @@ const HEALTH_CHECK_POLLING_INTERVAL: Duration = Duration::from_millis(100); // default bound on the number of computations it can perform simultaneously const DEFAULT_PARALLELISM: usize = 2; + #[allow(clippy::upper_case_acronyms)] type AVM = aquamarine::AVMRunner; @@ -305,7 +308,7 @@ pub fn aqua_vm_config( } = vm_config; let persistent_dir = persistent_dir(&tmp_dir); - let air_interpreter = air_interpreter_path(&persistent_dir ); + let air_interpreter = air_interpreter_path(&persistent_dir); write_default_air_interpreter(&air_interpreter).expect("write air interpreter"); VmConfig::new(peer_id, air_interpreter, None) @@ -331,7 +334,7 @@ pub async fn create_swarm_with_runtime( let node_listen_span = tracing::info_span!(parent: &parent_span, "config"); let node_creation_span = tracing::info_span!(parent: &parent_span, "config"); - let (node, management_kp) = config_apply_span.in_scope(||{ + let (node, management_kp) = config_apply_span.in_scope(|| { let tmp_dir = config.tmp_dir.path().to_path_buf(); let node_config = json!({ @@ -426,7 +429,7 @@ pub async fn create_swarm_with_runtime( "some version", system_service_distros, ); - (node, management_kp) + (node, management_kp) }); let mut node = node From ff66e957ede8b2fd95dfeb3a2e59bad912b56dc3 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 01:08:24 +0300 Subject: [PATCH 26/53] fix --- nox/src/node.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nox/src/node.rs b/nox/src/node.rs index 71a2792ac3..4d41c68f40 100644 --- a/nox/src/node.rs +++ b/nox/src/node.rs @@ -648,7 +648,7 @@ mod tests { use connected_client::ConnectedClient; use core_manager::manager::DummyCoreManager; use fs_utils::to_abs_path; - use server_config::{default_base_dir, load_config_with_args}; + use server_config::{default_base_dir, load_config_with_args, persistent_dir}; use system_services::SystemServiceDistros; use crate::Node; @@ -657,8 +657,10 @@ mod tests { async fn run_node() { log_utils::enable_logs(); let base_dir = default_base_dir(); + let persistent_dir = persistent_dir(&base_dir); fs_utils::create_dir(&base_dir).unwrap(); - write_default_air_interpreter(&air_interpreter_path(&base_dir)).unwrap(); + fs_utils::create_dir(&persistent_dir).unwrap(); + write_default_air_interpreter(&air_interpreter_path(&persistent_dir)).unwrap(); let mut config = load_config_with_args(vec![], None) .expect("Could not load config") From 4e0fa00a6e137298c7ec07de0b80a5a340c28224 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 15:40:29 +0300 Subject: [PATCH 27/53] change layout + inject persistent dirs into services --- Cargo.lock | 1 + crates/server-config/src/defaults.rs | 4 +- crates/server-config/src/dir_config.rs | 8 ++- crates/server-config/src/node_config.rs | 6 +- crates/server-config/src/resolved_config.rs | 2 +- crates/server-config/src/services_config.rs | 24 ++++--- crates/spell-service-api/src/lib.rs | 11 +-- nox/src/node.rs | 3 +- particle-execution/Cargo.toml | 1 + particle-execution/src/particle_vault.rs | 18 ++--- particle-services/src/app_services.rs | 74 ++++++++++++++++++--- 11 files changed, 112 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5762bf042..a83470e463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5644,6 +5644,7 @@ version = "0.1.0" dependencies = [ "async-trait", "bs58", + "eyre", "fluence-app-service", "fluence-libp2p", "fs-utils", diff --git a/crates/server-config/src/defaults.rs b/crates/server-config/src/defaults.rs index 4e14cf890b..c57847a792 100644 --- a/crates/server-config/src/defaults.rs +++ b/crates/server-config/src/defaults.rs @@ -120,9 +120,9 @@ pub fn default_keypair_path(base_dir: &Path) -> PathOrValue { } } -pub fn default_builtins_keypair_path(base_dir: &Path) -> PathOrValue { +pub fn default_builtins_keypair_path(persistent_base_dir: &Path) -> PathOrValue { PathOrValue::Path { - path: base_dir.join("builtins_secret_key.ed25519"), + path: persistent_base_dir.join("builtins_secret_key.ed25519"), } } diff --git a/crates/server-config/src/dir_config.rs b/crates/server-config/src/dir_config.rs index f5e7747201..806d9fdb44 100644 --- a/crates/server-config/src/dir_config.rs +++ b/crates/server-config/src/dir_config.rs @@ -120,7 +120,7 @@ impl UnresolvedDirConfig { ]) .context("creating configured directories")?; - let base = canonicalize(base)?; + let base_dir = canonicalize(base)?; // ephemeral dirs let avm_base_dir = canonicalize(avm_base_dir)?; let services_ephemeral_dir = canonicalize(services_ephemeral_dir)?; @@ -134,7 +134,9 @@ impl UnresolvedDirConfig { let cc_events_dir = canonicalize(cc_events_dir)?; Ok(ResolvedDirConfig { - base_dir: base, + base_dir, + ephemeral_base_dir, + persistent_base_dir, avm_base_dir, services_ephemeral_dir, services_persistent_dir, @@ -151,6 +153,8 @@ impl UnresolvedDirConfig { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ResolvedDirConfig { pub base_dir: PathBuf, + pub ephemeral_base_dir: PathBuf, + pub persistent_base_dir: PathBuf, /// Directory where particle's prev_data is stored pub avm_base_dir: PathBuf, pub services_ephemeral_dir: PathBuf, diff --git a/crates/server-config/src/node_config.rs b/crates/server-config/src/node_config.rs index 2a5a129396..367e559140 100644 --- a/crates/server-config/src/node_config.rs +++ b/crates/server-config/src/node_config.rs @@ -140,7 +140,7 @@ pub struct UnresolvedNodeConfig { } impl UnresolvedNodeConfig { - pub fn resolve(mut self, base_dir: &Path) -> eyre::Result { + pub fn resolve(mut self, persistent_base_dir: &Path) -> eyre::Result { self.load_system_services_envs(); let bootstrap_nodes = match self.local { @@ -151,12 +151,12 @@ impl UnresolvedNodeConfig { let root_key_pair = self .root_key_pair .unwrap_or_default() - .get_keypair(default_keypair_path(base_dir))?; + .get_keypair(default_keypair_path(persistent_base_dir))?; let builtins_key_pair = self .builtins_key_pair .unwrap_or_default() - .get_keypair(default_builtins_keypair_path(base_dir))?; + .get_keypair(default_builtins_keypair_path(persistent_base_dir))?; let mut allowed_binaries = self.allowed_binaries; allowed_binaries.push(self.system_services.aqua_ipfs.ipfs_binary_path.clone()); diff --git a/crates/server-config/src/resolved_config.rs b/crates/server-config/src/resolved_config.rs index a2ac112d39..18e8c7dce5 100644 --- a/crates/server-config/src/resolved_config.rs +++ b/crates/server-config/src/resolved_config.rs @@ -46,8 +46,8 @@ pub struct UnresolvedConfig { impl UnresolvedConfig { pub fn resolve(self) -> eyre::Result { - let node_config = self.node_config.resolve(&self.dir_config.base_dir)?; let dir_config = self.dir_config.resolve()?; + let node_config = self.node_config.resolve(&dir_config.persistent_base_dir)?; Ok(ResolvedConfig { dir_config, diff --git a/crates/server-config/src/services_config.rs b/crates/server-config/src/services_config.rs index 697813c919..77c685f9d4 100644 --- a/crates/server-config/src/services_config.rs +++ b/crates/server-config/src/services_config.rs @@ -30,8 +30,10 @@ pub struct ServicesConfig { /// Opaque environment variables to be passed on each service creation /// TODO: isolate envs of different modules (i.e., module A shouldn't access envs of module B) pub envs: HashMap, - /// Working dir for services - pub workdir: PathBuf, + /// Persistent working dir for services + pub persistent_work_dir: PathBuf, + /// Ephemeral working dir for services + pub ephemeral_work_dir: PathBuf, /// Dir to store .wasm modules and their configs pub modules_dir: PathBuf, /// Dir to persist info about running services @@ -53,7 +55,8 @@ impl ServicesConfig { #[allow(clippy::too_many_arguments)] pub fn new( local_peer_id: PeerId, - base_dir: PathBuf, + persistent_dir: PathBuf, + ephemeral_dir: PathBuf, particles_vault_dir: PathBuf, envs: HashMap, management_peer_id: PeerId, @@ -61,7 +64,8 @@ impl ServicesConfig { default_service_memory_limit: Option, allowed_binaries: Vec, ) -> Result { - let base_dir = to_abs_path(base_dir); + let persistent_dir = to_abs_path(persistent_dir); + let ephemeral_dir = to_abs_path(ephemeral_dir); let allowed_binaries = allowed_binaries .into_iter() @@ -78,10 +82,11 @@ impl ServicesConfig { let this = Self { local_peer_id, - blueprint_dir: config_utils::blueprint_dir(&base_dir), - workdir: config_utils::workdir(&base_dir), - modules_dir: config_utils::modules_dir(&base_dir), - services_dir: config_utils::services_dir(&base_dir), + blueprint_dir: config_utils::blueprint_dir(&persistent_dir), + persistent_work_dir: config_utils::workdir(&persistent_dir), + ephemeral_work_dir: config_utils::workdir(&ephemeral_dir), + modules_dir: config_utils::modules_dir(&persistent_dir), + services_dir: config_utils::services_dir(&persistent_dir), particles_vault_dir, envs, management_peer_id, @@ -92,7 +97,8 @@ impl ServicesConfig { create_dirs(&[ &this.blueprint_dir, - &this.workdir, + &this.persistent_work_dir, + &this.ephemeral_work_dir, &this.modules_dir, &this.services_dir, &this.particles_vault_dir, diff --git a/crates/spell-service-api/src/lib.rs b/crates/spell-service-api/src/lib.rs index 95e7ce1f9c..1c754c9400 100644 --- a/crates/spell-service-api/src/lib.rs +++ b/crates/spell-service-api/src/lib.rs @@ -302,10 +302,12 @@ mod tests { management_pid: PeerId, base_dir: PathBuf, ) -> (ParticleAppServices, ModuleRepository, PeerId) { + let persistent_dir = base_dir.join("persistent"); + let ephemeral_dir = base_dir.join("ephemeral"); let root_key_pair = Keypair::generate_ed25519(); - let vault_dir = base_dir.join("..").join("vault"); - let keypairs_dir = base_dir.join("..").join("keypairs"); - let workers_dir = base_dir.join("..").join("workers"); + let vault_dir = ephemeral_dir.join("..").join("vault"); + let keypairs_dir = persistent_dir.join("..").join("keypairs"); + let workers_dir = persistent_dir.join("..").join("workers"); let root_key_pair: KeyPair = root_key_pair.clone().into(); @@ -333,7 +335,8 @@ mod tests { let service_memory_limit = server_config::default_service_memory_limit(); let config = ServicesConfig::new( root_key_pair.get_peer_id(), - base_dir, + persistent_dir, + ephemeral_dir, vault_dir, HashMap::new(), management_pid, diff --git a/nox/src/node.rs b/nox/src/node.rs index 4d41c68f40..f97bc44679 100644 --- a/nox/src/node.rs +++ b/nox/src/node.rs @@ -147,7 +147,8 @@ impl Node { let services_config = ServicesConfig::new( scopes.get_host_peer_id(), - config.dir_config.services_persistent_dir.clone(), + config.dir_config.persistent_base_dir.clone(), + config.dir_config.ephemeral_base_dir.clone(), config_utils::particles_vault_dir(&config.dir_config.avm_base_dir), config.services_envs.clone(), config.management_peer_id, diff --git a/particle-execution/Cargo.toml b/particle-execution/Cargo.toml index 420012f5a9..763a1630de 100644 --- a/particle-execution/Cargo.toml +++ b/particle-execution/Cargo.toml @@ -21,3 +21,4 @@ bs58 = { workspace = true } tokio = { workspace = true, features = ["fs"] } parking_lot = { workspace = true } async-trait = { workspace = true } +eyre = { workspace = true } \ No newline at end of file diff --git a/particle-execution/src/particle_vault.rs b/particle-execution/src/particle_vault.rs index 253895a355..47b9566827 100644 --- a/particle-execution/src/particle_vault.rs +++ b/particle-execution/src/particle_vault.rs @@ -14,7 +14,8 @@ * limitations under the License. */ -use fluence_app_service::{MarineWASIConfig, ModuleDescriptor}; +use eyre::eyre; +use fluence_app_service::ModuleDescriptor; use std::io::ErrorKind; use std::path; use std::path::{Path, PathBuf}; @@ -164,19 +165,20 @@ impl ParticleVault { /// Map `vault_dir` to `/tmp/vault` inside the service. /// Particle File Vaults will be available as `/tmp/vault/$particle_id` - pub fn inject_vault(&self, module: &mut ModuleDescriptor) { - let wasi = &mut module.config.wasi; - if wasi.is_none() { - *wasi = Some(MarineWASIConfig::default()); - } - // SAFETY: set wasi to Some in the code above - let wasi = wasi.as_mut().unwrap(); + pub fn inject_vault(&self, module: &mut ModuleDescriptor) -> eyre::Result<()> { + let wasi = module + .config + .wasi + .as_mut() + .ok_or(eyre!("Could not inject vault into empty WASI config"))?; let vault_dir = self.vault_dir.to_path_buf(); wasi.preopened_files.insert(vault_dir.clone()); wasi.mapped_dirs .insert(VIRTUAL_PARTICLE_VAULT_PREFIX.into(), vault_dir); + + Ok(()) } } diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index 83f9fc402d..dfe733aede 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -14,13 +14,15 @@ * limitations under the License. */ use std::ops::Deref; +use std::path::PathBuf; use std::time::{Duration, Instant}; use std::{collections::HashMap, sync::Arc}; use derivative::Derivative; +use eyre::eyre; use fluence_app_service::{ AppService, AppServiceConfig, AppServiceError, CallParameters, MarineConfig, MarineError, - SecurityTetraplet, ServiceInterface, + MarineWASIConfig, ModuleDescriptor, SecurityTetraplet, ServiceInterface, }; use humantime_serde::re::humantime::format_duration as pretty; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; @@ -991,19 +993,68 @@ impl ParticleAppServices { } } + fn inject_default_wasi(&self, module: &mut ModuleDescriptor) { + let wasi = &mut module.config.wasi; + if wasi.is_none() { + *wasi = Some(MarineWASIConfig::default()); + } + } + + fn inject_persistent_dirs( + &self, + module: &mut ModuleDescriptor, + persistent_dir: PathBuf, + ) -> eyre::Result<()> { + let wasi = module.config.wasi.as_mut().ok_or(eyre!( + "Could not inject persistent dirs into empty WASI config" + ))?; + wasi.preopened_files.insert(persistent_dir.clone()); + wasi.mapped_dirs + .insert("/storage".into(), persistent_dir.clone()); + wasi.mapped_dirs.insert( + "/storage/module".into(), + persistent_dir.join(&module.import_name).clone(), + ); + Ok(()) + } + + fn inject_ephemeral_dirs( + &self, + module: &mut ModuleDescriptor, + ephemeral_dir: PathBuf, + ) -> eyre::Result<()> { + let wasi = module.config.wasi.as_mut().ok_or(eyre!( + "Could not inject ephemeral dirs into empty WASI config" + ))?; + wasi.preopened_files.insert(ephemeral_dir.clone()); + wasi.mapped_dirs + .insert("/tmp".into(), ephemeral_dir.clone()); + wasi.mapped_dirs.insert( + "/tmp/module".into(), + ephemeral_dir.join(&module.import_name).clone(), + ); + Ok(()) + } + fn create_app_service( &self, blueprint_id: String, service_id: String, ) -> Result { let mut modules_config = self.modules.resolve_blueprint(&blueprint_id)?; - modules_config - .iter_mut() - .for_each(|module| self.vault.inject_vault(module)); + modules_config.iter_mut().for_each(|module| { + self.inject_default_wasi(module); + // SAFETY: set wasi to Some in the code before calling inject_vault + self.vault.inject_vault(module).unwrap(); + self.inject_persistent_dirs(module, self.config.persistent_work_dir.join(&service_id)) + .unwrap(); + self.inject_ephemeral_dirs(module, self.config.ephemeral_work_dir.join(&service_id)) + .unwrap(); + }); let app_config = AppServiceConfig { - service_working_dir: self.config.workdir.join(&service_id), - service_base_dir: self.config.workdir.clone(), + service_working_dir: self.config.ephemeral_work_dir.join(&service_id), + service_base_dir: self.config.ephemeral_work_dir.clone(), marine_config: MarineConfig { // TODO: add an option to set individual per-service limit total_memory_limit: self @@ -1095,9 +1146,11 @@ mod tests { management_pid: PeerId, base_dir: PathBuf, ) -> ParticleAppServices { - let vault_dir = base_dir.join("..").join("vault"); - let keypairs_dir = base_dir.join("..").join("keypairs"); - let workers_dir = base_dir.join("..").join("workers"); + let persistent_dir = base_dir.join("persistent"); + let ephemeral_dir = base_dir.join("ephemeral"); + let vault_dir = ephemeral_dir.join("..").join("vault"); + let keypairs_dir = persistent_dir.join("..").join("keypairs"); + let workers_dir = persistent_dir.join("..").join("workers"); let service_memory_limit = server_config::default_service_memory_limit(); let key_storage = KeyStorage::from_path(keypairs_dir.clone(), root_keypair.clone().into()) .await @@ -1124,7 +1177,8 @@ mod tests { let config = ServicesConfig::new( PeerId::from(root_keypair.public()), - base_dir, + persistent_dir, + ephemeral_dir, vault_dir, HashMap::new(), management_pid, From 4f5b165f6dd3fcbd391d704f2aab8c5edcafe451 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 16:42:06 +0300 Subject: [PATCH 28/53] fix test --- particle-services/src/app_services.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index dfe733aede..3b62838ddf 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -1301,7 +1301,7 @@ mod tests { let inter1 = pas.get_interface(PeerScope::Host, service_id1, "").unwrap(); // delete module and check that interfaces will be returned anyway - let dir = modules_dir(base_dir.path()); + let dir = modules_dir(&base_dir.path().to_path_buf().join("persistent")); let module_file = dir.join(format!("{m_hash}.wasm")); tokio::fs::remove_file(module_file.clone()).await.unwrap(); From f460be5ac693a46b3d2dee270dbb01064c71c617 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 17:31:57 +0300 Subject: [PATCH 29/53] Expose applied config in CreatedSwarm --- crates/created-swarm/src/swarm.rs | 49 ++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/crates/created-swarm/src/swarm.rs b/crates/created-swarm/src/swarm.rs index ea5f3e9c1e..4654b7aa54 100644 --- a/crates/created-swarm/src/swarm.rs +++ b/crates/created-swarm/src/swarm.rs @@ -40,7 +40,8 @@ use futures::stream::iter; use nox::{Connectivity, Node}; use particle_protocol::ProtocolConfig; use server_config::{ - persistent_dir, system_services_config, BootstrapConfig, ChainListenerConfig, UnresolvedConfig, + persistent_dir, system_services_config, BootstrapConfig, ChainListenerConfig, ResolvedConfig, + UnresolvedConfig, }; use tempfile::TempDir; use test_constants::{EXECUTION_TIMEOUT, TRANSPORT_TIMEOUT}; @@ -59,6 +60,7 @@ type AVM = aquamarine::AVMRunner; #[derive(Derivative)] #[derivative(Debug)] pub struct CreatedSwarm { + pub config: ResolvedConfig, pub peer_id: PeerId, pub multiaddr: Multiaddr, // tmp dir, must be cleaned @@ -158,7 +160,17 @@ where F: (FnMut( Vec, Multiaddr, - ) -> BoxFuture<'static, (PeerId, Box>, KeyPair, SwarmConfig, Span)>) + ) -> BoxFuture< + 'static, + ( + PeerId, + Box>, + KeyPair, + SwarmConfig, + ResolvedConfig, + Span, + ), + >) + 'static + Send, M: (FnMut() -> Multiaddr) + 'static + Send, @@ -176,7 +188,8 @@ where let bootstraps = bootstraps(addrs); let create_node_future = create_node(bootstraps, addr.clone()); async move { - let (peer_id, node, management_keypair, config, span) = create_node_future.await; + let (peer_id, node, management_keypair, input_config, resolved_config, span) = + create_node_future.await; let connectivity = node.connectivity.clone(); let aquamarine_api = node.aquamarine_api.clone(); let started_node = node @@ -188,9 +201,10 @@ where .http_listen_addr .expect("could not take http listen addr"); CreatedSwarm { + config: resolved_config, peer_id, - multiaddr: config.listen_on, - tmp_dir: config.tmp_dir.clone(), + multiaddr: input_config.listen_on, + tmp_dir: input_config.tmp_dir.clone(), management_keypair, exit_outlet: started_node.exit_outlet, connectivity, @@ -317,7 +331,14 @@ pub fn aqua_vm_config( pub async fn create_swarm_with_runtime( config: SwarmConfig, vm_config: impl Fn(BaseVmConfig) -> RT::Config, -) -> (PeerId, Box>, KeyPair, SwarmConfig, Span) { +) -> ( + PeerId, + Box>, + KeyPair, + SwarmConfig, + ResolvedConfig, + Span, +) { use serde_json::json; let format = match &config.keypair { @@ -334,7 +355,7 @@ pub async fn create_swarm_with_runtime( let node_listen_span = tracing::info_span!(parent: &parent_span, "config"); let node_creation_span = tracing::info_span!(parent: &parent_span, "config"); - let (node, management_kp) = config_apply_span.in_scope(|| { + let (node, management_kp, resolved_config) = config_apply_span.in_scope(|| { let tmp_dir = config.tmp_dir.path().to_path_buf(); let node_config = json!({ @@ -421,7 +442,7 @@ pub async fn create_swarm_with_runtime( .extend(config.extend_system_services.clone()); let core_manager = Arc::new(DummyCoreManager::default().into()); let node = Node::new( - resolved, + resolved.clone(), core_manager, vm_config, data_store_config, @@ -429,7 +450,7 @@ pub async fn create_swarm_with_runtime( "some version", system_service_distros, ); - (node, management_kp) + (node, management_kp, resolved) }); let mut node = node @@ -444,6 +465,7 @@ pub async fn create_swarm_with_runtime( node, management_kp, config, + resolved_config, parent_span.clone(), ) }) @@ -451,6 +473,13 @@ pub async fn create_swarm_with_runtime( pub async fn create_swarm( config: SwarmConfig, -) -> (PeerId, Box>, KeyPair, SwarmConfig, Span) { +) -> ( + PeerId, + Box>, + KeyPair, + SwarmConfig, + ResolvedConfig, + Span, +) { create_swarm_with_runtime(config, aqua_vm_config).await } From 3588d1f423e33b6ae68a326d8d6d83d69caf2663 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 21 Feb 2024 19:11:51 +0300 Subject: [PATCH 30/53] rename stepper --- crates/server-config/src/defaults.rs | 2 +- crates/server-config/src/node_config.rs | 4 ++-- docker/Config.default.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/server-config/src/defaults.rs b/crates/server-config/src/defaults.rs index c57847a792..f37a5072f6 100644 --- a/crates/server-config/src/defaults.rs +++ b/crates/server-config/src/defaults.rs @@ -111,7 +111,7 @@ pub fn services_dir(base_dir: &Path) -> PathBuf { } pub fn avm_base_dir(base_dir: &Path) -> PathBuf { - base_dir.join("stepper") + base_dir.join("avm") } pub fn default_keypair_path(base_dir: &Path) -> PathOrValue { diff --git a/crates/server-config/src/node_config.rs b/crates/server-config/src/node_config.rs index 367e559140..3ed180fa0b 100644 --- a/crates/server-config/src/node_config.rs +++ b/crates/server-config/src/node_config.rs @@ -82,7 +82,7 @@ pub struct UnresolvedNodeConfig { #[serde(default)] pub protocol_config: ProtocolConfig, - /// Number of stepper VMs to create. By default, `num_cpus::get() * 2` is used + /// Number of AVMs to create. By default, `num_cpus::get() * 2` is used #[serde(default = "default_aquavm_pool_size")] pub aquavm_pool_size: usize, @@ -333,7 +333,7 @@ pub struct NodeConfig { pub protocol_config: ProtocolConfig, - /// Number of stepper VMs to create. By default, `num_cpus::get() * 2` is used + /// Number of AVMs to create. By default, `num_cpus::get() * 2` is used pub aquavm_pool_size: usize, /// Maximum heap size in bytes available for an interpreter instance. diff --git a/docker/Config.default.toml b/docker/Config.default.toml index 6c50fb3eda..8da2c83966 100644 --- a/docker/Config.default.toml +++ b/docker/Config.default.toml @@ -1,7 +1,7 @@ # # directories for nox persistent storage # base_dir = "/.fluence" # services_base_dir = "/services" -# avm_base_dir = "/stepper" +# avm_base_dir = "/avm" # spell_base_dir = "/spell" # keypairs_base_dir = "/keypairs" # workers_base_dir = "/workers" From 0dca554733022dd0c0a509bf39542d49fb8b0aa3 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 01:06:32 +0300 Subject: [PATCH 31/53] fix --- particle-services/src/app_services.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index 3b62838ddf..b40836daaf 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -1053,7 +1053,7 @@ impl ParticleAppServices { }); let app_config = AppServiceConfig { - service_working_dir: self.config.ephemeral_work_dir.join(&service_id), + service_working_dir: self.config.persistent_work_dir.join(&service_id), service_base_dir: self.config.ephemeral_work_dir.clone(), marine_config: MarineConfig { // TODO: add an option to set individual per-service limit From d8faeccc6aface95178d01d98c5a9a82f790825d Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 10:36:04 +0300 Subject: [PATCH 32/53] fix fd in core manager --- crates/core-manager/src/manager.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index 54def08f0b..7aa4325e04 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -1,5 +1,5 @@ use std::collections::{BTreeSet, HashMap}; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::hash::BuildHasherDefault; use std::io::Write; use std::ops::Deref; @@ -431,9 +431,16 @@ impl CoreManagerFunctions for PersistentCoreManager { .map_err(|err| PersistError::SerializationError { err })?; let exists = self.file_path.exists(); let mut file = if exists { - File::open(self.file_path.clone()).map_err(|err| PersistError::IoError { err })? + OpenOptions::new() + .write(true) + .open(self.file_path.clone()) + .map_err(|err| PersistError::IoError { err })? } else { - File::create(self.file_path.clone()).map_err(|err| PersistError::IoError { err })? + OpenOptions::new() + .write(true) + .create(true) + .open(self.file_path.clone()) + .map_err(|err| PersistError::IoError { err })? }; file.write(toml.as_bytes()) .map_err(|err| PersistError::IoError { err })?; From 110f582887b3da0dbac6355e8026b1de3d42abfb Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 10:37:49 +0300 Subject: [PATCH 33/53] fix fd in core manager --- crates/core-manager/src/manager.rs | 32 ++++++++++-------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index 7aa4325e04..eda2ac5d08 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -25,7 +25,7 @@ use crate::types::{AcquireRequest, Assignment, WorkType}; type Map = HashMap>; type MultiMap = multimap::MultiMap>; type BiMap = - bimap::BiHashMap, BuildHasherDefault>; +bimap::BiHashMap, BuildHasherDefault>; /// The `CoreManagerFunctions` trait defines operations for managing CPU cores. /// @@ -252,8 +252,8 @@ pub struct PersistenceTask { impl PersistenceTask { async fn process_events(stream: Src, core_manager: Arc) - where - Src: futures::Stream + Unpin + Send + Sync + 'static, + where + Src: futures::Stream + Unpin + Send + Sync + 'static, { let core_manager = core_manager.clone(); // We are not interested in the content of the event @@ -429,19 +429,7 @@ impl CoreManagerFunctions for PersistentCoreManager { drop(lock); let toml = toml::to_string_pretty(&persistent_state) .map_err(|err| PersistError::SerializationError { err })?; - let exists = self.file_path.exists(); - let mut file = if exists { - OpenOptions::new() - .write(true) - .open(self.file_path.clone()) - .map_err(|err| PersistError::IoError { err })? - } else { - OpenOptions::new() - .write(true) - .create(true) - .open(self.file_path.clone()) - .map_err(|err| PersistError::IoError { err })? - }; + let mut file = File::create(self.file_path.clone()).map_err(|err| PersistError::IoError { err })?; file.write(toml.as_bytes()) .map_err(|err| PersistError::IoError { err })?; Ok(()) @@ -506,15 +494,15 @@ mod tests { 2, CoreRange::default(), ) - .unwrap(); + .unwrap(); let init_id_1 = ::from_hex( "54ae1b506c260367a054f80800a545f23e32c6bc4a8908c9a794cb8dad23e5ea", ) - .unwrap(); + .unwrap(); let init_id_2 = ::from_hex( "1cce3d08f784b11d636f2fb55adf291d43c2e9cbe7ae7eeb2d0301a96be0a3a0", ) - .unwrap(); + .unwrap(); let unit_ids = vec![init_id_1, init_id_2]; let assignment_1 = manager .acquire_worker_core(AcquireRequest { @@ -549,7 +537,7 @@ mod tests { system_cpu_count, CoreRange::default(), ) - .unwrap(); + .unwrap(); let before_lock = manager.state.read(); let before_available_core = before_lock.available_cores.clone(); @@ -567,11 +555,11 @@ mod tests { let init_id_1 = ::from_hex( "54ae1b506c260367a054f80800a545f23e32c6bc4a8908c9a794cb8dad23e5ea", ) - .unwrap(); + .unwrap(); let init_id_2 = ::from_hex( "1cce3d08f784b11d636f2fb55adf291d43c2e9cbe7ae7eeb2d0301a96be0a3a0", ) - .unwrap(); + .unwrap(); let unit_ids = vec![init_id_1, init_id_2]; let assignment = manager .acquire_worker_core(AcquireRequest { From 11774a4a02bc066a44f5df6d6bfb54517c80257f Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 16:35:49 +0300 Subject: [PATCH 34/53] fix fd in core manager --- crates/core-manager/src/manager.rs | 23 ++++++++++++----------- crates/workers/src/workers.rs | 11 ++++++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/crates/core-manager/src/manager.rs b/crates/core-manager/src/manager.rs index eda2ac5d08..d0ba09d79a 100644 --- a/crates/core-manager/src/manager.rs +++ b/crates/core-manager/src/manager.rs @@ -1,5 +1,5 @@ use std::collections::{BTreeSet, HashMap}; -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::hash::BuildHasherDefault; use std::io::Write; use std::ops::Deref; @@ -25,7 +25,7 @@ use crate::types::{AcquireRequest, Assignment, WorkType}; type Map = HashMap>; type MultiMap = multimap::MultiMap>; type BiMap = -bimap::BiHashMap, BuildHasherDefault>; + bimap::BiHashMap, BuildHasherDefault>; /// The `CoreManagerFunctions` trait defines operations for managing CPU cores. /// @@ -252,8 +252,8 @@ pub struct PersistenceTask { impl PersistenceTask { async fn process_events(stream: Src, core_manager: Arc) - where - Src: futures::Stream + Unpin + Send + Sync + 'static, + where + Src: futures::Stream + Unpin + Send + Sync + 'static, { let core_manager = core_manager.clone(); // We are not interested in the content of the event @@ -429,7 +429,8 @@ impl CoreManagerFunctions for PersistentCoreManager { drop(lock); let toml = toml::to_string_pretty(&persistent_state) .map_err(|err| PersistError::SerializationError { err })?; - let mut file = File::create(self.file_path.clone()).map_err(|err| PersistError::IoError { err })?; + let mut file = + File::create(self.file_path.clone()).map_err(|err| PersistError::IoError { err })?; file.write(toml.as_bytes()) .map_err(|err| PersistError::IoError { err })?; Ok(()) @@ -494,15 +495,15 @@ mod tests { 2, CoreRange::default(), ) - .unwrap(); + .unwrap(); let init_id_1 = ::from_hex( "54ae1b506c260367a054f80800a545f23e32c6bc4a8908c9a794cb8dad23e5ea", ) - .unwrap(); + .unwrap(); let init_id_2 = ::from_hex( "1cce3d08f784b11d636f2fb55adf291d43c2e9cbe7ae7eeb2d0301a96be0a3a0", ) - .unwrap(); + .unwrap(); let unit_ids = vec![init_id_1, init_id_2]; let assignment_1 = manager .acquire_worker_core(AcquireRequest { @@ -537,7 +538,7 @@ mod tests { system_cpu_count, CoreRange::default(), ) - .unwrap(); + .unwrap(); let before_lock = manager.state.read(); let before_available_core = before_lock.available_cores.clone(); @@ -555,11 +556,11 @@ mod tests { let init_id_1 = ::from_hex( "54ae1b506c260367a054f80800a545f23e32c6bc4a8908c9a794cb8dad23e5ea", ) - .unwrap(); + .unwrap(); let init_id_2 = ::from_hex( "1cce3d08f784b11d636f2fb55adf291d43c2e9cbe7ae7eeb2d0301a96be0a3a0", ) - .unwrap(); + .unwrap(); let unit_ids = vec![init_id_1, init_id_2]; let assignment = manager .acquire_worker_core(AcquireRequest { diff --git a/crates/workers/src/workers.rs b/crates/workers/src/workers.rs index 15cb64e467..c9c6f44814 100644 --- a/crates/workers/src/workers.rs +++ b/crates/workers/src/workers.rs @@ -283,12 +283,21 @@ impl Workers { let removed_worker_info = worker_infos.remove(&worker_id); let removed_runtime = runtimes.remove(&worker_id); + drop(worker_ids); + drop(worker_infos); + drop(runtimes); + debug_assert!(removed_worker_id.is_some(), "worker_id does not exist"); debug_assert!(removed_worker_info.is_some(), "worker info does not exist"); debug_assert!(removed_runtime.is_some(), "worker info does not exist"); if let Some(runtime) = removed_runtime { - runtime.shutdown_background(); + // we can't shutdown the runtime in the async context, shift it to the blocking pool + // also we don't wait the result + tokio::task::Builder::new() + .name(&format!("runtime-shutdown-{}", worker_id)) + .spawn_blocking(move || runtime.shutdown_background()) + .expect("Could not spawn task"); } Ok(()) From 227362740952b2d6dad4d976467cec5b83924b11 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 17:00:00 +0300 Subject: [PATCH 35/53] update spell --- Cargo.toml | 2 +- particle-services/src/app_services.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 74523fd3d7..837eb77ac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ fluence-spell-dtos = "=0.7.3" fluence-spell-distro = "=0.7.3" # marine -fluence-app-service = { version = "0.33.0" } +fluence-app-service = { git = "https://github.com/fluencelabs/marine", branch = "feat/absolute_wasi_paths" } marine-utils = "0.5.1" marine-it-parser = "0.15.1" diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index b40836daaf..b1ec6ab5e2 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -453,7 +453,7 @@ impl ParticleAppServices { peer_pk: st.peer_pk, service_id: st.service_id, function_name: st.function_name, - lambda: st.json_path, + lens: st.json_path, }) .collect() }) From b9f903784cc69a0cbb8bebcdc8e8ef311e76f78e Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 17:35:37 +0300 Subject: [PATCH 36/53] update spell --- nox/src/node.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nox/src/node.rs b/nox/src/node.rs index f97bc44679..ecbd205b8a 100644 --- a/nox/src/node.rs +++ b/nox/src/node.rs @@ -147,8 +147,8 @@ impl Node { let services_config = ServicesConfig::new( scopes.get_host_peer_id(), - config.dir_config.persistent_base_dir.clone(), - config.dir_config.ephemeral_base_dir.clone(), + config.dir_config.services_persistent_dir.clone(), + config.dir_config.services_ephemeral_dir.clone(), config_utils::particles_vault_dir(&config.dir_config.avm_base_dir), config.services_envs.clone(), config.management_peer_id, From c86d21017ee9310fbf1aab88ac60978f5692817a Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 19:40:06 +0300 Subject: [PATCH 37/53] shutdown workers runtime at shutdown signal --- crates/nox-tests/tests/tetraplets.rs | 6 +++--- crates/workers/src/workers.rs | 20 ++++++++++++++++++++ nox/src/node.rs | 7 +++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/nox-tests/tests/tetraplets.rs b/crates/nox-tests/tests/tetraplets.rs index 172fa4207c..80e578e9b2 100644 --- a/crates/nox-tests/tests/tetraplets.rs +++ b/crates/nox-tests/tests/tetraplets.rs @@ -95,7 +95,7 @@ async fn test_tetraplets() { let tetraplet = &ap_literal_tetraplets[0][0]; assert_eq!(tetraplet.function_name, ""); assert_eq!(tetraplet.peer_pk, client.peer_id.to_base58()); - assert_eq!(tetraplet.lambda, ""); + assert_eq!(tetraplet.lens, ""); assert_eq!(tetraplet.service_id, ""); let first_tetraplets = args.next().unwrap(); @@ -108,7 +108,7 @@ async fn test_tetraplets() { let tetraplet = &first_tetraplets[0][0]; assert_eq!(tetraplet.function_name, "identity"); assert_eq!(tetraplet.peer_pk, client.node.to_base58()); - assert_eq!(tetraplet.lambda, ""); + assert_eq!(tetraplet.lens, ""); assert_eq!(tetraplet.service_id, "op"); let ap_first_tetraplets = args.next().unwrap(); @@ -127,6 +127,6 @@ async fn test_tetraplets() { let tetraplet = &second_tetraplets[0][0]; assert_eq!(tetraplet.function_name, "get_tetraplets"); assert_eq!(tetraplet.peer_pk, client.node.to_base58()); - assert_eq!(tetraplet.lambda, ".$.[0].[0].peer_pk"); + assert_eq!(tetraplet.lens, ".$.[0].[0].peer_pk"); assert_eq!(tetraplet.service_id, tetraplets_service.id.as_str()); } diff --git a/crates/workers/src/workers.rs b/crates/workers/src/workers.rs index c9c6f44814..b2f6a30ef3 100644 --- a/crates/workers/src/workers.rs +++ b/crates/workers/src/workers.rs @@ -506,6 +506,26 @@ impl Workers { } } } + + pub fn shutdown(&self) { + let mut runtimes = self.runtimes.write(); + let mut deleted_runtimes = Vec::with_capacity(runtimes.len()); + let worker_ids: Vec = runtimes.keys().cloned().collect(); + for worker_id in worker_ids { + if let Some(runtime) = runtimes.remove(&worker_id) { + deleted_runtimes.push(runtime); + } + } + + tokio::task::Builder::new() + .name("workers-shutdown") + .spawn_blocking(move || { + for runtime in deleted_runtimes { + runtime.shutdown_background(); + } + }) + .expect("Could not spawn a task"); + } } #[cfg(test)] diff --git a/nox/src/node.rs b/nox/src/node.rs index ecbd205b8a..6ef0bd7abf 100644 --- a/nox/src/node.rs +++ b/nox/src/node.rs @@ -100,6 +100,8 @@ pub struct Node { versions: Versions, pub chain_listener: Option, + + workers: Arc, } impl Node { @@ -390,6 +392,7 @@ impl Node { allow_local_addresses, versions, chain_listener, + workers.clone(), )) } @@ -485,6 +488,7 @@ impl Node { allow_local_addresses: bool, versions: Versions, chain_listener: Option, + workers: Arc, ) -> Box { let node_service = Self { particle_stream, @@ -511,6 +515,7 @@ impl Node { allow_local_addresses, versions, chain_listener, + workers, }; Box::new(node_service) @@ -539,6 +544,7 @@ impl Node { let libp2p_metrics = self.libp2p_metrics; let allow_local_addresses = self.allow_local_addresses; let versions = self.versions; + let workers = self.workers.clone(); let chain_listener = self.chain_listener; task::Builder::new().name(&task_name.clone()).spawn(async move { @@ -589,6 +595,7 @@ impl Node { dispatcher.cancel().await; connectivity.cancel().await; aquamarine_backend.abort(); + workers.shutdown(); }.in_current_span()).expect("Could not spawn task"); // Note: need to be after the start of the node to be able to subscribe spells From d89b0e349176674ef5ab20ffaf7b8a8b8190af35 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:35:11 +0300 Subject: [PATCH 38/53] fix --- Cargo.lock | 279 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 209 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a83470e463..f2d6b81838 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,9 +1770,9 @@ dependencies = [ [[package]] name = "decider-distro" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd525ce56be3ee02d1c91bb4cbb09e0e10797f3f3f584187c89c8cdcd0ec65ff" +checksum = "6fc977928aedc97953c9bd41627dd498c7cfd06e63b55b62464be45f6299287e" dependencies = [ "built 0.7.1", "fluence-spell-dtos", @@ -2186,16 +2186,15 @@ dependencies = [ [[package]] name = "fluence-app-service" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5358cd011d91885b81539bc36f8f67075cd6bb1bf260c7ab35a7ff31b5870d" +version = "0.34.0" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" dependencies = [ "log", "maplit", - "marine-min-it-version", - "marine-runtime 0.34.0", - "marine-wasm-backend-traits", - "marine-wasmtime-backend", + "marine-min-it-version 0.3.2 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-runtime 0.35.0", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "serde", "serde_derive", "serde_json", @@ -3264,6 +3263,18 @@ dependencies = [ "wasmer-interface-types-fl", ] +[[package]] +name = "it-json-serde" +version = "0.5.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "serde", + "serde_derive", + "serde_json", + "thiserror", + "wasmer-interface-types-fl", +] + [[package]] name = "it-lilo" version = "0.6.0" @@ -4308,6 +4319,17 @@ dependencies = [ "serde", ] +[[package]] +name = "marine-call-parameters" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05495180730abae04abe209386ce367309a82110edb65fcdb1f3080f819bc1a0" +dependencies = [ + "marine-macro 0.14.0", + "marine-rs-sdk-main 0.14.0", + "serde", +] + [[package]] name = "marine-core" version = "0.26.1" @@ -4320,14 +4342,14 @@ dependencies = [ "it-memory-traits", "log", "marine-it-generator 0.13.1", - "marine-it-interfaces", - "marine-it-parser", - "marine-min-it-version", + "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-min-it-version 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "marine-module-info-parser 0.11.1", - "marine-module-interface", - "marine-utils", - "marine-wasm-backend-traits", - "marine-wasmtime-backend", + "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-utils 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasmtime-backend 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "multimap 0.8.3", "once_cell", "paste", @@ -4339,24 +4361,23 @@ dependencies = [ [[package]] name = "marine-core" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9a08bd99ea96356abdac57700a58496dc4034d1f48000fb54e532fc5021c8d" +version = "0.29.0" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" dependencies = [ "anyhow", "bytesize", "it-lilo", "it-memory-traits", "log", - "marine-it-generator 0.15.0", - "marine-it-interfaces", - "marine-it-parser", - "marine-min-it-version", - "marine-module-info-parser 0.13.0", - "marine-module-interface", - "marine-utils", - "marine-wasm-backend-traits", - "marine-wasmtime-backend", + "marine-it-generator 0.16.0", + "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-it-parser 0.15.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-min-it-version 0.3.2 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-module-info-parser 0.14.0", + "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-utils 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "multimap 0.8.3", "once_cell", "paste", @@ -4373,7 +4394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03328e174951faf739e7e12be3d9ffb044cfc308508dc89260d63a1a43a0df" dependencies = [ "it-lilo", - "marine-it-parser", + "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "marine-macro-impl 0.7.1", "once_cell", "serde", @@ -4385,13 +4406,12 @@ dependencies = [ [[package]] name = "marine-it-generator" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64902f7fee0488f2b1e27e8f595772f1a7a386d07f74e55a07def1ad967a64fd" +version = "0.16.0" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" dependencies = [ "it-lilo", - "marine-it-parser", - "marine-macro-impl 0.13.0", + "marine-it-parser 0.15.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-macro-impl 0.14.0", "once_cell", "serde", "serde_json", @@ -4410,6 +4430,15 @@ dependencies = [ "wasmer-interface-types-fl", ] +[[package]] +name = "marine-it-interfaces" +version = "0.9.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "multimap 0.8.3", + "wasmer-interface-types-fl", +] + [[package]] name = "marine-it-parser" version = "0.15.1" @@ -4418,9 +4447,27 @@ checksum = "921afa7f139791b24c4cb91c918d1ef7411ef40c3b801309ec43f6b2c89b107b" dependencies = [ "anyhow", "itertools 0.10.5", - "marine-it-interfaces", - "marine-module-interface", - "marine-wasm-backend-traits", + "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", + "semver 1.0.20", + "serde", + "thiserror", + "walrus", + "wasmer-interface-types-fl", +] + +[[package]] +name = "marine-it-parser" +version = "0.15.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "nom", "semver 1.0.20", "serde", @@ -4459,6 +4506,16 @@ dependencies = [ "marine-rs-sdk-main 0.13.0", ] +[[package]] +name = "marine-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f502185316f584a9373cceb6ff24a11d260dfd39505c817056bc127cd1a96a08" +dependencies = [ + "marine-macro-impl 0.14.0", + "marine-rs-sdk-main 0.14.0", +] + [[package]] name = "marine-macro-impl" version = "0.7.1" @@ -4511,6 +4568,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "marine-macro-impl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50fbc0e70ee4cde7802f0748acfb197d7770c7feffb980ce8c29bddd007519e" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", +] + [[package]] name = "marine-min-it-version" version = "0.3.2" @@ -4521,6 +4591,15 @@ dependencies = [ "semver 1.0.20", ] +[[package]] +name = "marine-min-it-version" +version = "0.3.2" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "once_cell", + "semver 1.0.20", +] + [[package]] name = "marine-module-info-parser" version = "0.11.1" @@ -4531,7 +4610,7 @@ dependencies = [ "chrono", "derivative", "marine-rs-sdk-main 0.10.3", - "marine-wasm-backend-traits", + "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 1.0.20", "serde", "thiserror", @@ -4540,15 +4619,14 @@ dependencies = [ [[package]] name = "marine-module-info-parser" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e39ef4b727e6e895b0b53682dc35463e00004aa0089d49797b4f289227ea3b5d" +version = "0.14.0" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" dependencies = [ "anyhow", "chrono", "derivative", - "marine-rs-sdk-main 0.13.0", - "marine-wasm-backend-traits", + "marine-rs-sdk-main 0.14.0", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "semver 1.0.20", "serde", "thiserror", @@ -4563,7 +4641,23 @@ checksum = "d92d2243bf0d3aea6401d9e57a1ee17677b624337981322e0153cc2d54744080" dependencies = [ "anyhow", "itertools 0.10.5", - "marine-it-interfaces", + "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", + "semver 1.0.20", + "serde", + "thiserror", + "walrus", + "wasmer-interface-types-fl", +] + +[[package]] +name = "marine-module-interface" +version = "0.8.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "nom", "semver 1.0.20", "serde", @@ -4600,14 +4694,14 @@ dependencies = [ [[package]] name = "marine-rs-sdk" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f33834365d1e6dd041e821a758000f32bed5b960edb09f9d8c61e340dd82e29" +checksum = "f93d2bd852fea1fea8097c195044430347eda98fd6a3752119b549192d5ac4ba" dependencies = [ - "marine-call-parameters 0.13.0", - "marine-macro 0.13.0", - "marine-rs-sdk-main 0.13.0", - "marine-timestamp-macro 0.13.0", + "marine-call-parameters 0.14.0", + "marine-macro 0.14.0", + "marine-rs-sdk-main 0.14.0", + "marine-timestamp-macro 0.14.0", "serde", ] @@ -4641,6 +4735,16 @@ dependencies = [ "serde", ] +[[package]] +name = "marine-rs-sdk-main" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b79c165fc21438b069babeec5ae36ba0eade5e08fb1d92dabbe6b41014ce841" +dependencies = [ + "log", + "serde", +] + [[package]] name = "marine-runtime" version = "0.32.1" @@ -4648,17 +4752,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c9a539b0b1586839f133f768db670f0b3f6cd5ffd977dc247cbf8acf8a3dc1a" dependencies = [ "bytesize", - "it-json-serde", + "it-json-serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "it-memory-traits", "itertools 0.10.5", "log", "marine-core 0.26.1", - "marine-module-interface", + "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "marine-rs-sdk 0.10.3", "marine-rs-sdk-main 0.10.3", - "marine-utils", - "marine-wasm-backend-traits", - "marine-wasmtime-backend", + "marine-utils 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasmtime-backend 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot", "safe-transmute", "serde", @@ -4672,24 +4776,24 @@ dependencies = [ [[package]] name = "marine-runtime" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26c68703d0dc853790adf27813734a696dc75182a93c2a7b3d24a86eed56254" +version = "0.35.0" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" dependencies = [ "bytesize", - "it-json-serde", + "it-json-serde 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "it-memory-traits", "itertools 0.10.5", "log", "marine-call-parameters 0.10.3", "marine-call-parameters 0.12.0", - "marine-core 0.28.0", - "marine-module-interface", - "marine-rs-sdk 0.13.0", - "marine-rs-sdk-main 0.13.0", - "marine-utils", - "marine-wasm-backend-traits", - "marine-wasmtime-backend", + "marine-call-parameters 0.13.0", + "marine-core 0.29.0", + "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-rs-sdk 0.14.0", + "marine-rs-sdk-main 0.14.0", + "marine-utils 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "parking_lot", "safe-transmute", "serde", @@ -4733,9 +4837,9 @@ dependencies = [ [[package]] name = "marine-timestamp-macro" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5086357724dc2a65e7abe39c85d58b9971198f8678ebc642a69aae1bbc7ae0" +checksum = "d03f267ac0a29f543ef12a1a519ff8d98e74ac66e1c580f2930d41ce2c50507d" dependencies = [ "chrono", "quote", @@ -4747,6 +4851,11 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce56bfabfd0af5326ff81c32c8d2261aa03b10e00ea6c165de4ebf8a3f998e4" +[[package]] +name = "marine-utils" +version = "0.5.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" + [[package]] name = "marine-wasm-backend-traits" version = "0.5.1" @@ -4762,6 +4871,20 @@ dependencies = [ "wasmparser 0.101.1", ] +[[package]] +name = "marine-wasm-backend-traits" +version = "0.5.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "anyhow", + "it-memory-traits", + "multimap 0.8.3", + "paste", + "thiserror", + "wasmer-interface-types-fl", + "wasmparser 0.101.1", +] + [[package]] name = "marine-wasmtime-backend" version = "0.5.1" @@ -4771,7 +4894,23 @@ dependencies = [ "anyhow", "it-memory-traits", "log", - "marine-wasm-backend-traits", + "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "multimap 0.8.3", + "paste", + "wasmer-interface-types-fl", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "marine-wasmtime-backend" +version = "0.5.1" +source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +dependencies = [ + "anyhow", + "it-memory-traits", + "log", + "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", "multimap 0.8.3", "paste", "wasmer-interface-types-fl", @@ -5674,7 +5813,7 @@ dependencies = [ "json-utils", "libipld", "log", - "marine-it-parser", + "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot", "particle-args", "particle-execution", From 4da2cbcc1f42c044cc992c6c82b3557681bf6cc6 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 23:19:21 +0300 Subject: [PATCH 39/53] fluence-spell-dtos update --- Cargo.lock | 181 +++++++++-------------- Cargo.toml | 2 +- particle-builtins/src/builtins.rs | 2 - particle-execution/src/particle_vault.rs | 1 - particle-services/src/app_services.rs | 5 +- 5 files changed, 71 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2d6b81838..136613b0ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2186,15 +2186,16 @@ dependencies = [ [[package]] name = "fluence-app-service" -version = "0.34.0" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f1b1edfa366551382890ecf2b18d8b9675b3a105bf2d90c444446cc081115d" dependencies = [ "log", "maplit", - "marine-min-it-version 0.3.2 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-runtime 0.35.0", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-min-it-version", + "marine-runtime 0.36.0", + "marine-wasm-backend-traits 0.6.0", + "marine-wasmtime-backend 0.6.0", "serde", "serde_derive", "serde_json", @@ -3263,18 +3264,6 @@ dependencies = [ "wasmer-interface-types-fl", ] -[[package]] -name = "it-json-serde" -version = "0.5.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" -dependencies = [ - "serde", - "serde_derive", - "serde_json", - "thiserror", - "wasmer-interface-types-fl", -] - [[package]] name = "it-lilo" version = "0.6.0" @@ -4342,14 +4331,14 @@ dependencies = [ "it-memory-traits", "log", "marine-it-generator 0.13.1", - "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-min-it-version 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-it-interfaces", + "marine-it-parser 0.15.1", + "marine-min-it-version", "marine-module-info-parser 0.11.1", - "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-utils 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-wasmtime-backend 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-module-interface", + "marine-utils", + "marine-wasm-backend-traits 0.5.1", + "marine-wasmtime-backend 0.5.1", "multimap 0.8.3", "once_cell", "paste", @@ -4361,23 +4350,24 @@ dependencies = [ [[package]] name = "marine-core" -version = "0.29.0" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2989c1670a82d69724804c7af5d8e4c35231083338806c49485d983f27dd440f" dependencies = [ "anyhow", "bytesize", "it-lilo", "it-memory-traits", "log", - "marine-it-generator 0.16.0", - "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-it-parser 0.15.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-min-it-version 0.3.2 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-module-info-parser 0.14.0", - "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-utils 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-it-generator 0.17.0", + "marine-it-interfaces", + "marine-it-parser 0.16.0", + "marine-min-it-version", + "marine-module-info-parser 0.15.0", + "marine-module-interface", + "marine-utils", + "marine-wasm-backend-traits 0.6.0", + "marine-wasmtime-backend 0.6.0", "multimap 0.8.3", "once_cell", "paste", @@ -4394,7 +4384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03328e174951faf739e7e12be3d9ffb044cfc308508dc89260d63a1a43a0df" dependencies = [ "it-lilo", - "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-it-parser 0.15.1", "marine-macro-impl 0.7.1", "once_cell", "serde", @@ -4406,11 +4396,12 @@ dependencies = [ [[package]] name = "marine-it-generator" -version = "0.16.0" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993c33c6df3ae978022058dfd63a33b52de1307adec59e0c3f3367dd06bdf342" dependencies = [ "it-lilo", - "marine-it-parser 0.15.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-it-parser 0.16.0", "marine-macro-impl 0.14.0", "once_cell", "serde", @@ -4430,15 +4421,6 @@ dependencies = [ "wasmer-interface-types-fl", ] -[[package]] -name = "marine-it-interfaces" -version = "0.9.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" -dependencies = [ - "multimap 0.8.3", - "wasmer-interface-types-fl", -] - [[package]] name = "marine-it-parser" version = "0.15.1" @@ -4447,9 +4429,9 @@ checksum = "921afa7f139791b24c4cb91c918d1ef7411ef40c3b801309ec43f6b2c89b107b" dependencies = [ "anyhow", "itertools 0.10.5", - "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-it-interfaces", + "marine-module-interface", + "marine-wasm-backend-traits 0.5.1", "nom", "semver 1.0.20", "serde", @@ -4460,14 +4442,15 @@ dependencies = [ [[package]] name = "marine-it-parser" -version = "0.15.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa9ba677486e1f1bf2bfd3e34b259255df277887fa90d5c22acb258bdedc34e" dependencies = [ "anyhow", "itertools 0.10.5", - "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-it-interfaces", + "marine-module-interface", + "marine-wasm-backend-traits 0.6.0", "nom", "semver 1.0.20", "serde", @@ -4591,15 +4574,6 @@ dependencies = [ "semver 1.0.20", ] -[[package]] -name = "marine-min-it-version" -version = "0.3.2" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" -dependencies = [ - "once_cell", - "semver 1.0.20", -] - [[package]] name = "marine-module-info-parser" version = "0.11.1" @@ -4610,7 +4584,7 @@ dependencies = [ "chrono", "derivative", "marine-rs-sdk-main 0.10.3", - "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasm-backend-traits 0.5.1", "semver 1.0.20", "serde", "thiserror", @@ -4619,14 +4593,15 @@ dependencies = [ [[package]] name = "marine-module-info-parser" -version = "0.14.0" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db5fe45b6f0f397ecce757c23a3d0c4a08c2e9ffa6e7f642cb89e1ec777b16d" dependencies = [ "anyhow", "chrono", "derivative", "marine-rs-sdk-main 0.14.0", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasm-backend-traits 0.6.0", "semver 1.0.20", "serde", "thiserror", @@ -4641,23 +4616,7 @@ checksum = "d92d2243bf0d3aea6401d9e57a1ee17677b624337981322e0153cc2d54744080" dependencies = [ "anyhow", "itertools 0.10.5", - "marine-it-interfaces 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nom", - "semver 1.0.20", - "serde", - "thiserror", - "walrus", - "wasmer-interface-types-fl", -] - -[[package]] -name = "marine-module-interface" -version = "0.8.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "marine-it-interfaces 0.9.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-it-interfaces", "nom", "semver 1.0.20", "serde", @@ -4752,17 +4711,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c9a539b0b1586839f133f768db670f0b3f6cd5ffd977dc247cbf8acf8a3dc1a" dependencies = [ "bytesize", - "it-json-serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "it-json-serde", "it-memory-traits", "itertools 0.10.5", "log", "marine-core 0.26.1", - "marine-module-interface 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-module-interface", "marine-rs-sdk 0.10.3", "marine-rs-sdk-main 0.10.3", - "marine-utils 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "marine-wasmtime-backend 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-utils", + "marine-wasm-backend-traits 0.5.1", + "marine-wasmtime-backend 0.5.1", "parking_lot", "safe-transmute", "serde", @@ -4776,24 +4735,25 @@ dependencies = [ [[package]] name = "marine-runtime" -version = "0.35.0" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e428f8066ed3cbd6def39a818ffe949d7ab15559666927b25401068ce2655d" dependencies = [ "bytesize", - "it-json-serde 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "it-json-serde", "it-memory-traits", "itertools 0.10.5", "log", "marine-call-parameters 0.10.3", "marine-call-parameters 0.12.0", "marine-call-parameters 0.13.0", - "marine-core 0.29.0", - "marine-module-interface 0.8.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-core 0.30.0", + "marine-module-interface", "marine-rs-sdk 0.14.0", "marine-rs-sdk-main 0.14.0", - "marine-utils 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", - "marine-wasmtime-backend 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-utils", + "marine-wasm-backend-traits 0.6.0", + "marine-wasmtime-backend 0.6.0", "parking_lot", "safe-transmute", "serde", @@ -4851,11 +4811,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce56bfabfd0af5326ff81c32c8d2261aa03b10e00ea6c165de4ebf8a3f998e4" -[[package]] -name = "marine-utils" -version = "0.5.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" - [[package]] name = "marine-wasm-backend-traits" version = "0.5.1" @@ -4873,8 +4828,9 @@ dependencies = [ [[package]] name = "marine-wasm-backend-traits" -version = "0.5.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ba17135a13735abfc8fbbb221cd27fb8c607482ec72d7cd8f37315c9fdf6c5" dependencies = [ "anyhow", "it-memory-traits", @@ -4894,7 +4850,7 @@ dependencies = [ "anyhow", "it-memory-traits", "log", - "marine-wasm-backend-traits 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-wasm-backend-traits 0.5.1", "multimap 0.8.3", "paste", "wasmer-interface-types-fl", @@ -4904,13 +4860,14 @@ dependencies = [ [[package]] name = "marine-wasmtime-backend" -version = "0.5.1" -source = "git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths#1b7702331af868d8a4e1e4b9e680ecb088e9a88b" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7a74c532073142ced94fdeaf7a3e84f92ec5ebef5751fbd5516185ae3cf211" dependencies = [ "anyhow", "it-memory-traits", "log", - "marine-wasm-backend-traits 0.5.1 (git+https://github.com/fluencelabs/marine?branch=feat/absolute_wasi_paths)", + "marine-wasm-backend-traits 0.6.0", "multimap 0.8.3", "paste", "wasmer-interface-types-fl", @@ -5813,7 +5770,7 @@ dependencies = [ "json-utils", "libipld", "log", - "marine-it-parser 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "marine-it-parser 0.15.1", "parking_lot", "particle-args", "particle-execution", diff --git a/Cargo.toml b/Cargo.toml index 837eb77ac7..59f0ae6df3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ fluence-spell-dtos = "=0.7.3" fluence-spell-distro = "=0.7.3" # marine -fluence-app-service = { git = "https://github.com/fluencelabs/marine", branch = "feat/absolute_wasi_paths" } +fluence-app-service = "0.35.0" marine-utils = "0.5.1" marine-it-parser = "0.15.1" diff --git a/particle-builtins/src/builtins.rs b/particle-builtins/src/builtins.rs index a8aaa91c6d..d101e8893a 100644 --- a/particle-builtins/src/builtins.rs +++ b/particle-builtins/src/builtins.rs @@ -1020,7 +1020,6 @@ fn make_module_config(args: Args) -> Result { let _max_heap_size: Option = Args::next_opt("max_heap_size", &mut args)?; let logger_enabled = Args::next_opt("logger_enabled", &mut args)?; - let preopened_files = Args::next_opt("preopened_files", &mut args)?; let envs = Args::next_opt("envs", &mut args)?.map(table); let mapped_dirs = Args::next_opt("mapped_dirs", &mut args)?.map(table); let mounted_binaries = Args::next_opt("mounted_binaries", &mut args)?.map(table); @@ -1033,7 +1032,6 @@ fn make_module_config(args: Args) -> Result { config: ModuleConfig { logger_enabled, wasi: Some(WASIConfig { - preopened_files, envs, mapped_dirs, }), diff --git a/particle-execution/src/particle_vault.rs b/particle-execution/src/particle_vault.rs index 47b9566827..b4c2fa8626 100644 --- a/particle-execution/src/particle_vault.rs +++ b/particle-execution/src/particle_vault.rs @@ -174,7 +174,6 @@ impl ParticleVault { let vault_dir = self.vault_dir.to_path_buf(); - wasi.preopened_files.insert(vault_dir.clone()); wasi.mapped_dirs .insert(VIRTUAL_PARTICLE_VAULT_PREFIX.into(), vault_dir); diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index b1ec6ab5e2..192e42018c 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -1008,7 +1008,6 @@ impl ParticleAppServices { let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject persistent dirs into empty WASI config" ))?; - wasi.preopened_files.insert(persistent_dir.clone()); wasi.mapped_dirs .insert("/storage".into(), persistent_dir.clone()); wasi.mapped_dirs.insert( @@ -1026,7 +1025,6 @@ impl ParticleAppServices { let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject ephemeral dirs into empty WASI config" ))?; - wasi.preopened_files.insert(ephemeral_dir.clone()); wasi.mapped_dirs .insert("/tmp".into(), ephemeral_dir.clone()); wasi.mapped_dirs.insert( @@ -1054,8 +1052,7 @@ impl ParticleAppServices { let app_config = AppServiceConfig { service_working_dir: self.config.persistent_work_dir.join(&service_id), - service_base_dir: self.config.ephemeral_work_dir.clone(), - marine_config: MarineConfig { + marine_config: MarineConfig { // TODO: add an option to set individual per-service limit total_memory_limit: self .config From 0b2ef5449cd3a103f1c8d79cb8cb11a19d45eead Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Feb 2024 23:43:46 +0300 Subject: [PATCH 40/53] fluence-spell-dtos update --- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/nox-tests/tests/tetraplets/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 136613b0ef..fc02c271ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5770,7 +5770,7 @@ dependencies = [ "json-utils", "libipld", "log", - "marine-it-parser 0.15.1", + "marine-it-parser 0.16.0", "parking_lot", "particle-args", "particle-execution", diff --git a/Cargo.toml b/Cargo.toml index 59f0ae6df3..52a44dbaec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ fluence-spell-distro = "=0.7.3" # marine fluence-app-service = "0.35.0" marine-utils = "0.5.1" -marine-it-parser = "0.15.1" +marine-it-parser = "0.16.0" # avm avm-server = "=0.35.0" diff --git a/crates/nox-tests/tests/tetraplets/Cargo.toml b/crates/nox-tests/tests/tetraplets/Cargo.toml index 82bf46fa24..57548652d4 100644 --- a/crates/nox-tests/tests/tetraplets/Cargo.toml +++ b/crates/nox-tests/tests/tetraplets/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Fluence Labs"] edition = "2021" [dependencies] -marine-rs-sdk = "0.13.0" +marine-rs-sdk = "0.14.0" once_cell = "1.19.0" [[bin]] From d242556da62469d00a085b2d999c4bbcc5c72330 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 09:59:27 +0300 Subject: [PATCH 41/53] fix --- particle-services/src/app_services.rs | 36 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index 192e42018c..b3bfdca54c 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -14,7 +14,7 @@ * limitations under the License. */ use std::ops::Deref; -use std::path::PathBuf; +use std::path::Path; use std::time::{Duration, Instant}; use std::{collections::HashMap, sync::Arc}; @@ -936,6 +936,7 @@ impl ParticleAppServices { let creation_start_time = Instant::now(); let service = self .create_app_service(blueprint_id.clone(), service_id.clone()) + .await .inspect_err(|_| { if let Some(metrics) = self.metrics.as_ref() { metrics.observe_created_failed(); @@ -1003,16 +1004,16 @@ impl ParticleAppServices { fn inject_persistent_dirs( &self, module: &mut ModuleDescriptor, - persistent_dir: PathBuf, + persistent_dir: &Path, ) -> eyre::Result<()> { let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject persistent dirs into empty WASI config" ))?; wasi.mapped_dirs - .insert("/storage".into(), persistent_dir.clone()); + .insert("/storage".into(), persistent_dir.to_path_buf()); wasi.mapped_dirs.insert( "/storage/module".into(), - persistent_dir.join(&module.import_name).clone(), + persistent_dir.join(&module.import_name), ); Ok(()) } @@ -1020,39 +1021,50 @@ impl ParticleAppServices { fn inject_ephemeral_dirs( &self, module: &mut ModuleDescriptor, - ephemeral_dir: PathBuf, + ephemeral_dir: &Path, ) -> eyre::Result<()> { let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject ephemeral dirs into empty WASI config" ))?; wasi.mapped_dirs - .insert("/tmp".into(), ephemeral_dir.clone()); + .insert("/tmp".into(), ephemeral_dir.to_path_buf()); wasi.mapped_dirs.insert( "/tmp/module".into(), - ephemeral_dir.join(&module.import_name).clone(), + ephemeral_dir.join(&module.import_name), ); Ok(()) } - fn create_app_service( + async fn create_app_service( &self, blueprint_id: String, service_id: String, ) -> Result { + let persistent_dir = self.config.persistent_work_dir.join(&service_id); + let ephemeral_dir = self.config.ephemeral_work_dir.join(&service_id); + + // TODO: introduce separate errors + tokio::fs::create_dir(&persistent_dir) + .await + .map_err(|err| InternalError(err.to_string()))?; + tokio::fs::create_dir(&ephemeral_dir) + .await + .map_err(|err| InternalError(err.to_string()))?; + let mut modules_config = self.modules.resolve_blueprint(&blueprint_id)?; modules_config.iter_mut().for_each(|module| { self.inject_default_wasi(module); // SAFETY: set wasi to Some in the code before calling inject_vault self.vault.inject_vault(module).unwrap(); - self.inject_persistent_dirs(module, self.config.persistent_work_dir.join(&service_id)) + self.inject_persistent_dirs(module, persistent_dir.as_path()) .unwrap(); - self.inject_ephemeral_dirs(module, self.config.ephemeral_work_dir.join(&service_id)) + self.inject_ephemeral_dirs(module, ephemeral_dir.as_path()) .unwrap(); }); let app_config = AppServiceConfig { - service_working_dir: self.config.persistent_work_dir.join(&service_id), - marine_config: MarineConfig { + service_working_dir: persistent_dir, + marine_config: MarineConfig { // TODO: add an option to set individual per-service limit total_memory_limit: self .config From e6770094a40f089502eacac043e48a20a7b7bc83 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 10:12:53 +0300 Subject: [PATCH 42/53] fix --- particle-services/src/app_services.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index b3bfdca54c..fa8269821d 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -1006,15 +1006,17 @@ impl ParticleAppServices { module: &mut ModuleDescriptor, persistent_dir: &Path, ) -> eyre::Result<()> { + let module_dir = persistent_dir.join(&module.import_name); + //todo: add error & make async + std::fs::create_dir_all(&module_dir)?; + let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject persistent dirs into empty WASI config" ))?; wasi.mapped_dirs .insert("/storage".into(), persistent_dir.to_path_buf()); - wasi.mapped_dirs.insert( - "/storage/module".into(), - persistent_dir.join(&module.import_name), - ); + wasi.mapped_dirs + .insert("/storage/module".into(), module_dir); Ok(()) } @@ -1023,6 +1025,10 @@ impl ParticleAppServices { module: &mut ModuleDescriptor, ephemeral_dir: &Path, ) -> eyre::Result<()> { + let module_dir = ephemeral_dir.join(&module.import_name); + //todo: add error & make async + std::fs::create_dir_all(&module_dir)?; + let wasi = module.config.wasi.as_mut().ok_or(eyre!( "Could not inject ephemeral dirs into empty WASI config" ))?; @@ -1030,7 +1036,7 @@ impl ParticleAppServices { .insert("/tmp".into(), ephemeral_dir.to_path_buf()); wasi.mapped_dirs.insert( "/tmp/module".into(), - ephemeral_dir.join(&module.import_name), + module_dir, ); Ok(()) } @@ -1042,12 +1048,12 @@ impl ParticleAppServices { ) -> Result { let persistent_dir = self.config.persistent_work_dir.join(&service_id); let ephemeral_dir = self.config.ephemeral_work_dir.join(&service_id); - + // TODO: introduce separate errors - tokio::fs::create_dir(&persistent_dir) + tokio::fs::create_dir_all(&persistent_dir) .await .map_err(|err| InternalError(err.to_string()))?; - tokio::fs::create_dir(&ephemeral_dir) + tokio::fs::create_dir_all(&ephemeral_dir) .await .map_err(|err| InternalError(err.to_string()))?; From 8fbec0eb0e4f94c7390f42e4bbeedd0208b47e25 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 10:31:03 +0300 Subject: [PATCH 43/53] fix --- crates/nox-tests/tests/modules.rs | 2 -- crates/nox-tests/tests/services.rs | 5 ----- crates/service-modules/src/modules/fixture.rs | 1 - 3 files changed, 8 deletions(-) diff --git a/crates/nox-tests/tests/modules.rs b/crates/nox-tests/tests/modules.rs index 15c46575fa..968dd3f252 100644 --- a/crates/nox-tests/tests/modules.rs +++ b/crates/nox-tests/tests/modules.rs @@ -41,7 +41,6 @@ async fn test_add_module_mounted_binaries() { "logger_enabled": true, "wasi": { "envs": json!({}), - "preopened_files": vec!["/tmp"], "mapped_dirs": json!({}), }, "mounted_binaries": json!({"cmd": "/usr/bin/curl"}) @@ -88,7 +87,6 @@ async fn test_add_module_mounted_binaries_forbidden() { "logger_enabled": true, "wasi": { "envs": json!({}), - "preopened_files": vec!["/tmp"], "mapped_dirs": json!({}), }, "mounted_binaries": json!({"cmd": "/usr/bin/behbehbeh"}) diff --git a/crates/nox-tests/tests/services.rs b/crates/nox-tests/tests/services.rs index 688c814328..b7955d94bc 100644 --- a/crates/nox-tests/tests/services.rs +++ b/crates/nox-tests/tests/services.rs @@ -150,11 +150,6 @@ async fn create_service_from_config() { "modules": [ { "name": "pure_base64", - "preopened_files": [ - [ - "tmp" - ] - ], "mem_pages_count": [ 123 ], diff --git a/crates/service-modules/src/modules/fixture.rs b/crates/service-modules/src/modules/fixture.rs index cdc664f9b5..9ce4e8cb61 100644 --- a/crates/service-modules/src/modules/fixture.rs +++ b/crates/service-modules/src/modules/fixture.rs @@ -34,7 +34,6 @@ pub fn module_config(import_name: &str) -> JValue { "logger_enabled": true, "wasi": { "envs": json!({}), - "preopened_files": vec!["/tmp"], "mapped_dirs": json!({}), } }) From b46c33a55ecb642c3311368142c9d17f7cf195f5 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 10:33:37 +0300 Subject: [PATCH 44/53] fix --- crates/nox-tests/tests/modules.rs | 3 +++ crates/nox-tests/tests/services.rs | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/nox-tests/tests/modules.rs b/crates/nox-tests/tests/modules.rs index 968dd3f252..8a0fe492c2 100644 --- a/crates/nox-tests/tests/modules.rs +++ b/crates/nox-tests/tests/modules.rs @@ -46,6 +46,9 @@ async fn test_add_module_mounted_binaries() { "mounted_binaries": json!({"cmd": "/usr/bin/curl"}) }); + let a = config.to_string(); + println!("{}", a); + let script = r#" (xor (seq diff --git a/crates/nox-tests/tests/services.rs b/crates/nox-tests/tests/services.rs index b7955d94bc..817180e162 100644 --- a/crates/nox-tests/tests/services.rs +++ b/crates/nox-tests/tests/services.rs @@ -163,9 +163,7 @@ async fn create_service_from_config() { "logging_mask": [ 4 ], - "envs": [ - [] - ], + "envs": {}, "mounted_binaries": [ [ [ @@ -330,9 +328,7 @@ async fn handle_same_dir_in_preopens_and_mapped_dirs() { "logging_mask": [ 4 ], - "envs": [ - [] - ], + "envs": {}, "mounted_binaries": [ [ [ From e8ca4a2acf44ce73b327538630833ff50d548bf0 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:15:58 +0300 Subject: [PATCH 45/53] fix --- crates/nox-tests/tests/file_share/Cargo.lock | 193 +++++++---- crates/nox-tests/tests/file_share/Cargo.toml | 8 +- .../file_share/artifacts/file_share.wasm | Bin 143043 -> 126041 bytes crates/nox-tests/tests/file_share/src/main.rs | 2 +- crates/nox-tests/tests/tetraplets/Cargo.lock | 322 ++++++++++++------ crates/nox-tests/tests/tetraplets/Cargo.toml | 2 + .../tetraplets/artifacts/tetraplets.wasm | Bin 77055 -> 94476 bytes 7 files changed, 345 insertions(+), 182 deletions(-) diff --git a/crates/nox-tests/tests/file_share/Cargo.lock b/crates/nox-tests/tests/file_share/Cargo.lock index e5554e26df..1efb764a73 100644 --- a/crates/nox-tests/tests/file_share/Cargo.lock +++ b/crates/nox-tests/tests/file_share/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -43,17 +49,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -96,7 +101,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.109", ] [[package]] @@ -113,7 +118,7 @@ checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -133,7 +138,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -192,18 +197,26 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "marine-call-parameters" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05495180730abae04abe209386ce367309a82110edb65fcdb1f3080f819bc1a0" dependencies = [ - "cfg-if", + "marine-macro", + "marine-rs-sdk-main", + "serde", ] [[package]] name = "marine-macro" -version = "0.7.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e03da22f641984aad5229f780d190502196d1c0bf908d3d17f5d6bcba73e525" +checksum = "f502185316f584a9373cceb6ff24a11d260dfd39505c817056bc127cd1a96a08" dependencies = [ "marine-macro-impl", "marine-rs-sdk-main", @@ -211,35 +224,35 @@ dependencies = [ [[package]] name = "marine-macro-impl" -version = "0.7.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca474b63cabaf8d7d9b38de87d630023cbc91ddc77e92f9c7bb745462a131b44" +checksum = "e50fbc0e70ee4cde7802f0748acfb197d7770c7feffb980ce8c29bddd007519e" dependencies = [ "proc-macro2", "quote", "serde", "serde_json", - "syn", + "syn 1.0.109", ] [[package]] name = "marine-rs-sdk" -version = "0.7.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfeeb7b8cd98e32276fabfe6ab095a6aae793f3f080e7eb1c3d36b1b762397c" +checksum = "f93d2bd852fea1fea8097c195044430347eda98fd6a3752119b549192d5ac4ba" dependencies = [ + "marine-call-parameters", "marine-macro", "marine-rs-sdk-main", "marine-timestamp-macro", - "polyplets", "serde", ] [[package]] name = "marine-rs-sdk-main" -version = "0.7.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43e6eac611bc5b96e80a3f3e2621eeded69fb56389aa83b6ea76ec0f243ef23" +checksum = "0b79c165fc21438b069babeec5ae36ba0eade5e08fb1d92dabbe6b41014ce841" dependencies = [ "log", "serde", @@ -247,24 +260,14 @@ dependencies = [ [[package]] name = "marine-timestamp-macro" -version = "0.7.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea4557a757e9f4d04a0b6afb047431a246963268a4cab56c62cb5355457cb2f" +checksum = "d03f267ac0a29f543ef12a1a519ff8d98e74ac66e1c580f2930d41ce2c50507d" dependencies = [ "chrono", "quote", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.15" @@ -280,17 +283,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" -[[package]] -name = "polyplets" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c051f220264e391afae06b6dceda199d523ee1dc7ae8e8cba4119a378dd411f" -dependencies = [ - "marine-macro", - "marine-rs-sdk-main", - "serde", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -299,18 +291,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -359,29 +351,29 @@ checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -390,9 +382,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -400,23 +392,23 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "syn" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ - "winapi-util", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "time" -version = "0.1.45" +name = "termcolor" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "winapi-util", ] [[package]] @@ -431,12 +423,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -464,7 +450,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -486,7 +472,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -527,3 +513,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" diff --git a/crates/nox-tests/tests/file_share/Cargo.toml b/crates/nox-tests/tests/file_share/Cargo.toml index f8f463314f..049d3fba82 100644 --- a/crates/nox-tests/tests/file_share/Cargo.toml +++ b/crates/nox-tests/tests/file_share/Cargo.toml @@ -1,3 +1,5 @@ +[workspace] + [package] name = "file_share" version = "0.1.0" @@ -8,9 +10,7 @@ edition = "2021" name = "file_share" path = "src/main.rs" -[workspace] - [dependencies] -marine-rs-sdk = "0.7.1" -rand = { workspace = true } +marine-rs-sdk = "0.14.0" +rand = "0.8.5" base64 = "0.21.0" diff --git a/crates/nox-tests/tests/file_share/artifacts/file_share.wasm b/crates/nox-tests/tests/file_share/artifacts/file_share.wasm index 5cd2e7dd1818196d0c509a4db3857ecdf5ad5926..9ac3ec38ef26e21bbf2df536f20628d4134ef311 100755 GIT binary patch literal 126041 zcmeFa51eJyRp)!oKKK8G(%p>`4e1`N@tG3GtMfWj?8 z=F3XQymmkac z%Zoe48~^ZH9Mil0<)g-fFMIG=*C*fcNkYRCNJ%dwHDeNK)fH+Y zuKng4SF`&3wb#Ds`olNhdgHY>z4^%Mwb$Qv^R-7`wvDW7-+28kw_JPM^@p#2(~YY) z9zK#~rvNwiO*dToO*h|iv zOJkUL_hTTr;6j9S}mYc(iC zfx4@=3hFmqORvo~=+Yjo@#z{}m9H0qS)+0LXj7q<)m@R31G~om z`qVq^)ato4ZXm$RP^{M*wIY^j)||_!<9OCx&TMWJE-MPaWf=|VZ-c+Cp|@Hyzb$_k zf9ge^ZE3Wb=wWSbJ?l2V+s!rJbmN=eeE3^j*0`22KD?U!Ro>OCx%t*>kG}cl8?qmC zTiys?uikj=(d+-kEvq&evTx64k1&Z~3nksQdN}(PH#d;-yoqkeHrrhdH*}L*N zO_fQA|L5tJ@{QLYxpCk5lNoy!5|ER#Ue>+#=38%Gz4`iE)ZG8&W;L-VaQbc6VwS)8 zjUb=xnw!Wp^nS;I#4R_{;I(i5#^H@`Ts@M#GjD$mVurl?&irBb2l;=?e;5$V>HKf=|CnF$;rvtX5A);h z)9xFuf55%leW&|V_h0f4xJTWi`Jd(g+MUS1-+iz99{2kH;4Z!Bf8;;l{;gYgce^{> zWA4w~ALSo(AJ6|He_#GH`3d)X`3LiF_&+}6p3Z+OzwD>oNAj=!Dfj33pS!=wzt6qL z{rCLu@}J8u{(blBZav@rj{Gm&PvpOl|ABkZeOo@bBmXXUm-}e`Z`>34H+^sZ-R@)g zzGw2^&VSMUocoOXdG`zM-@DJ|r@hO)&wZQw(fl=!yMO2Y(w*}g`7gQ7k3UuWlzU)r z-aU497Wyk?zAJM}oBs*fqPy#}OQoBkh&~=#3uQ5rg>3oQe)q^);AL&?_?mcazx%kp zeI8oFaND8q@K5~FPt=c;#XeOERN3bqt6o057P>I#gIV~{9UsZAn8`ynaN~rP@JSM> zGN@5eMfSP-t!{k3^Rpo1f&4zbgk$^Md+lWeab=%-U@a6&z7{_G+4XG5^FBR=4Hfyk z$3E-&tc4F+ne_2Ms_>z4iMrZS!3R^`{g#)2rF!d@mkRn}Sqp3KvYD)F_=mUJV~nWp$7}oCaeHz5G&;U8*h4|N zfbw09rO(0>`Kzj7d-|EBvJv_uZqwNC>|U{>Y=+`!$^W%b9J)kBsYaWJd~rDBN6LCA zR?8w$szxhD!bO*r#iDDKO*+RYG{Wt^{+2`g9BuUSuCMv#KG*V150LYMsA8Wmtu5YTFYj9iZ>X}@qjBxVBYH<7mri{YM;X~==&`scH$!R!2e}rUB zNM>Usi?gIYXVYEfT>Z}SCG zMmxF;ssQH&Y#qN1N(ZIw{lR~(bTAP3A20tUQyO}P_rfy?HEAzLA{rCyB@d;16WKiy|V4IO994v zOJlO=n3`8?r(2AEi^_DQ*Xvnv+AC>2v(_mI8kfufKJ>!iB>@3-TJ>Dxt_=PLj4>i6$FMNhyG3;Y^C}8pAw<0Tr8_9n*g>1D zVzlDHrYizN?864G&0J}X=8EqyTP~TA%+uVN=8{2;24NwQe}VV@ z!a#u({1jP((ceKYz&v8%+nN_en#Yh(eP!$x1`c4n^q56$F%0ih3_}Z%VHn+BucQ8K zV1yCFM3#sQ(|}>JDTaycB!)CL2i?dpZ9pcxN`}i;mqpF0G-XL+vWGqynuZh8ozL1( zGtV-jl+Y9wMJs8B5U)i7N@7E48~wWYC{9^k;v`N25eAO~zCk}y4mBDpWNq&;aVe}uE}LoeYF+da#8a=SN2Xke zbr~xGgTZLo5WvaO)0l{f6uEB$ORTMj1P~bK5%Jsu{MGt`rii&_xL_CuI8h9=N_ZJQ z09{)uKDZX>11OTrJKczXR6$B-F*)!8Z}QLRR_t{ zQq@z*Ky(0f!>i|R#NT3e=bTSw0l-&&9iju7<5myQAS^TA&{>TxbVr`d17Rp9O5Bc~mzZtT@4; zqNFZqT{}r|Fl8k;pqB)PX7EUKu>MNHfq^CKqdbp2+LZe0ekF?Q5UZYuRZqkkG7C(k zqk&0yvTjCY6dlhbx~L?HuEitDc9iBwOc+`1% zK1xGA6mno_NDmEAhyY!LXJsAuhUf+nWC^sM~~$plpwQ5`FThF49H zPUweu-vKzC*fC7y#awivv(GIEzM7W2B8h(WW4{)R5c4SmHAQ(85D4otk1)U>6^A$&;pNy4v!vqfPoZi8bh%zqGAoV!6btW0RXE&IGH&$tp^3V@&E!$ zF{TvLHY&~~>Sk7EXa>kAH9ggwx~*0<*ryV+E{a*~n#wFkOrXxl;EvYq{s{qag_-ul z5Qb<+n0T-<=5{-ry_s~3b01iURQgIuS%q>lF zTV~vrRos>(Zj-qPw`qlz(GW!gLKQaCtf$kmb0s!VM&LF{NSaIBmcebTa+}y@7%WzM zr!OM6)i&j}Hr!TQMLjic)3g)ud?Rt2wY&hNgbt4$c+jS}4IvD-)gre+H*s4Va<)g@ zhS4kaA8vyq#BEaB+ZDGlnX|GjVl$m1O^vy&IdxmDin)P|6(r{C#W^ASRKg+hXEh&X z^~#GPH7!*U!jY&@Ggh%a3A&~*%8o5Af^JGyQmJHOAQEI^q@@PBj5rW1D*GC>FOtKut?~*!m4ionjs| z@v5`eal zxh3DNI1o4+miI9S;(>w%w7gdwIO4yq@n2W`mx=#kQCr#Dz8m?^lnUc9^J$6yCEpdU7)Vu_Bo=n)r{;#H|R<5&xai>lnFk8x~l~>=3efpKl{?2>hnOEDhMUK8P z$QPX-biz}=bH^tegKqelKfd#Kn}c5X>yO^?1I1t_{NGQ0 z_jB!i>tLJj59a;W!D&2C=Xpld3l|1wQYa4D_Q4L`7p%h0!Si_EY46V;oaGk=-ftfa z{LVq?pFcR8?_GR<8Q(A9`<#TpVnS$lLhywN;TNU+b5s7Fl>g$Ce_qPpoAO_h@?Vt;_vL(F!S|I+;e-CA!}6sk{DuBy!}4V(7`fxVf5LCIw|6o~ zq5qb$@1Hj;x1NZjqlD-AvxenavGhSIk4yL7QX0q$tI{OAbQ<>a#-&?=y>$XM2^(O) zv??vm2u*>%a99G~`Mtw(?+JgvzhqdxB;ry#;hU-aR~qIuf99}ko``%V%wOtv49hcP zX-DO8X%ID#2ddH}?45>P;R0+BWx`CrCgG)OYz%wv7&d4-5g{gd=-p45uTBI8)Q%Hz z8VJp%K)ouW34z_Xw4n(FfMJs+kZ@3>7!O`W0$_txGxj)@CZX?NrN%VWPaj|u&}6TX&eMHZ=P7)ia_hKU|*onVoPHYja905D@0OOvo+ z5GU9Vgtv{9n^EVfSgud0Z>#}9^O#K2!f<#A~cy~r;Q%cZKc zVNM%k*n7sML9{MAbOf7(J)-3pc10C1fhR7SAi|y#&{$mc&ho1b&1Hdt>|b4>*%GMO zoCti;xU``OfnQveCcz1I8V)E@kpS3W)$&`0rLRhputfwL7{v0mB0@E1C=XXJCt?{_ zFKOUBTwpzb7?EJUJmKpJr^q5f!wC7~pu-~D#3E@JVTP&)l&%myw<=A-K~GEL{!XwT z2yesvR?=dGO+tMPyZoBTd1;6u)i3ccG#0$nzi7mH^C$di3FXKe7x@Q@?9 zSM!oVkC^xuoPgGGUPcyq<QY%E=z@V#+q z5Iw_paE?}`N$5;q0_=(^U;>9+HbDdss<`?a5?2@2mv5-hEHoUQ*f0dXEHzANLlds< zo$#?V3C7il1Q56)0j0sJP^ZqXN|SK9@P4^LEZ+d=5m!?luD&u6%eeY-Jf`vJ~50FTn(rS;n@lCanN5j8Wvzz!xCBK48h)4l{P}dBKuXRRwk76C{#$jcr>$7)gzfnz0A*P<8n@>bdCmm z!H^Bx9E0p*`Ov>CS`Q>C`s$;kyfoSsDdj;@UJ@;inBw%(svow+O zI^|raD&Y9aQAS5bm?vwmL4Jcuc}n2~xV$P9o&nZYjI?N&I*Nctc$!qjVMWvRKuUd_ z)Sdn{f}=u@@wwe!BTh;w50i4xzao7-z}GfJT`YY+Ut3VvS?~_N1z_(Yp4JE3e;nKZuhU70QE2_myAFi9QrdxkS-Yd(?@{L z$Av+kldm8lx+vhUuYm6o>^DrnUKp}K#13bE>{F=WQ@GHG{RI7ZzJJX`l}AXq(0@gO z|3Se*)y6@4tQ`{}ECy9p#^^Y!luI;Lf4HGUdhzeW3;m_et_}Q0z%@ zzuX+=@F3wsN@$Q^eh(6EV|bqek7g7J3G|0aX+?RFQl0{rb`&N`>Hgjh*FDy{-RFBU zM;H^2e9)GcY5EUt!sel}Guz1Oxhv|p)?0&)_H#Ns*xm2)z`@uXbhI7LUk0YLQg-kO z7O&b-_JU^*pPU)Fr`gbwtSKaUy2CwLR(8Ty$lpiE*)aS@rX+R<2zS6ehrdi)BAo*n zUm-h?WpiET=XFE8HQ8WJtwmJvJBuyciO0&n<`gqD0lj(TrpLy zwxQe^qjGHLO|)CzP;OyVu0B<+v7y|VqjGG03m(+ygyx1)+pUyn*rXILIzJZ&pg%PL zH7({_{QUCy@ABK04`BGu`_qkS^>2P~PzJ`~VUdr!n zsIOsE{!GfB*^sZ{Q~qqqpWTqJVN?EG%Aebip9V}LwRJp1eD&dUhK@1Z5*mo#e;z~C zpBSqChOVS}7`CLYZrPAu4b|3^zjZ@?HB{SD{+K{h-V$Lyfw*GS&4dMje zrue&VSQrkveujOD89)0fV(xMUPoHY;hwIpJd*}z(v;Gg~sQ4_3@#`La#kDa!s%%SO zhu+p#6Q)FODQs(o#rCaO2*gma!p>;NW~=h!9}?cG2%N22ZF3;LB`&(HYSD#%oNUOx zq;g!{H!ivTwB(L+t0i}aZ7WoUwB+{Fk~=O}Ex9wnO_hsF?zmjFQ|024J1$o(xig_YRW2^M<8sxKI};kQ9MtHiC3jpZiiJ9)Y*I>tIkSn*LnPSN z@Z@MDA$a_XBMIe+-CKKqHPHGb;M)?cgcj_7CV~ZA(Le}dK>vfIw+?1tANb2znB2@@ zYf|Vc9y`?&08SBw-GL?n3g_09K_^0;Q$(*@ifl;MJPPK^`&CTTK*kt-zTEHW4a5d;8Dk9FMxpxBAaV9oi~I zeru06@!(yfIRjtBuY2e$Vv`eM%KIUGNpoBueYxPmL)91XQ24j@+TB2qMtF209R+`@ zLZJzi3Qta?qtI_9=qA+pv-U%&4d9@_Z2U8>SUfcObI&Lpse6)Y-hFb` zwT~6IV-^u3=ZK~WJ1qLu<_Kn)3*W>ZBW9v|)s6v?Upm;LICcxaXvkP1<~=7&087eI zzbRXa$R0(7^9c9jXnU9&;_H!x`?SkU~IG^3wiw=!pxKWvkH~Pm_?!_C*5!V1LSZumFDzH!( zbL8vmrqRQz?`v>vL8G>+9-%m3X{Orj@#49DLY&|7a&P-LGi&vPu zUo=$|p0vqq!6$VRRi0OY|I&Ho`2qp2bT-bdvy?$Z^yJ*C6UNg(-DSt8g#*X5gD9cG zeU%h4{nliX^jnFs&Hu0#IAmXOTiIJFXQbEG&0~d1i()OEwIMl1L?kV-4j`dBJihf+ zGYTN>MES=fytir@`NwwawESyGi69vK=vZo*G(u6aWxq8gPfVb3q|>q}vdRg+#?ix5 zWlS)F&MNrTj&e+6G8@ImcZhyMuU81J++xRuv)qVa$BopXaHr4}X{>)xz!FS;*P*xd zCQ?qF)Jow9mcn;8^zuPQX9c(&9Re+ibI}8&>AK1buhp>k5bFz`J;mxheNVNZiC&LS zexc(%H*6FngN$AW4ti(JdG!pD=Y|!sHZ}-qQF)o9$_PVhvYTZ1p#&(7_aOMi6-xld zeG$_!(gfq94G7))M(DCahWkffTKYaZ`cgL^uD(9i%bUj_N)`h~v6XTb#kW}ql_R3F z%!U^8?l=nyym&>fP z)+o%u3MP=MP+wJ|Nyf&6Md{hmTLtlrzNA!8i0S2E&>CmeCX4C}2AfxQx`8vpglQ@! zlV=mkL?qPoPGX5i2P+f}NWUTi4C3^`iki-jDEwDUXyKQfgvOz#@JkS;yd6j8{8$XX z1bV+zq;c$5KY}Ivl8kv}pwDRAg+8Yy7tF>`u$)FP=*$hg>|&&QAX3y~8W~m;PIX3a zG3pZ5;)sIgiq54|Js+KlN0EdlAbO$EB1YOq&gUCW@?^vi$CErGpdM)DUVaKADPd|e(>35>?hl8%zg$0 z>rCutt+_NkHs`@&im`Me;vazr84=M>?SCmeNIxfdu$bgQ`a0r4pl{BDWEc;^oEvyh zoOBX@O|SwoBQYW)IO5LDDv3chIfjAC?_d&>9E#+pjN;BSxD?QA#2k)s=Z?dj^o2>; z8Zd9+c12h_J!CCQtf*DK<`i}=OJcKA7%raqVM@dk@{&ZVywm3~?3@Xb?jF!dj))r5 z9C<>y7!MCtv?}!A8qm|JA9l|af1X$P2s`0RDdNv*j&m3;p2M)yo@N3SOTJNB_`Wz_ z8%a(lF%Wz@i9tE7b#`Dro*!#SauwG2RD#> z+HE6PvYfc}`3}2$q}zQ@FK-+ZfsvS^Kn&O|;$niqX^RO1rnAeG$zvJM8EQIf$Nb^~ zQk~2rO*2^M2{tSsf~<3SEFUh)iFht=a`~W0wN6;+h6t6#*x_PwqUy*2#2GIZ3X+a= z(=8D?S~vO{FBZjkv0xI!D;;dH0Fq|Q#>FC0MD0&aR8@`wItu?&EEa^GPfS#c)vp!{ z){)Uf1$sJ#2Z!mbi1kac`kJWhW+K%zQOQGQsm}{qQ<)s;lq8D<%mmHtSQMNJBS`Yv ze1RD7gq;tuNvrjlK@k1ySd(tKVAvD1v|cbk6YB*GHFfe6y@?id26O~kte*~jGESSW z7Y1{*UI24yy@)Vv<*3jV^r8BqEm<9288cwTZ4wZhbDOg+C4K`Tp|^qGpdr-UNRA|T zDmJ7AsVh!wXstar8#=L}PI2hEA!asgSbdGzkV9s^h&oG^$q1vfAOegRHs~x_93i#; zrLf`jdf`sBUI2Y_HY6h*2HnVpCYj?9ZORH-FCs5$#jhB2Jg(zPS}*cS2OXbNvTgM= zbhcjPwq8in1vC;btrx{0*SIk+TLYfCq@K={@o3QqGpDDv__Fl^uxc$GQ_8~&_|h?@ zP0ol~<pRtEkigNJ{~hS-`@e ze!{NRg;n&R7&v3rs_gAwMA^505oOV9(gSC2q0CHR)gtf}u95PjM zbqn;ZcQ9a`nlPG^sVTm)Zfh2+bLJ3egr0mIdAJRFP?1MNoNG*?us4BV-r9j5fhe?Z zr2C-~$U?4Lo%5}}#igmS+dx0C?I+IzbY;kroeRFkOz1JOS=;9!*P6y7qQhs2g@ml8 zi*VNJo@f9opVV2#3+bXB^G3@Rh#YI5zE@PFCaV(MKg#!R)EHESGO)L7&P40FXpN<5 zv?j`lzGPquH>Iv7t`aRkRmCBuv5+&2Yhfo;o~mM%kt*P7MzjQrz4fBpll>Vr2^pOG z+NvEnuw&f8~nVh8J3Z^BX^*}uy z)0=xGM%Y($(hRpbONBj|%}0B)xn^&*1DW-0PgXyWF`g>dIFM;ujko53jI;aNpy5*w z%}G2XkzQH%f41|wZV7ynJ6-CW`~!gK0#zZDdPTqTaEINlwdN#meRgiP=D3#YN>-L< zbF^|2p3PIj=TgGws)Wyy5cMWJ8CR4|$YU;z+)iWT0y;O-9bZ@JdLJf+h5}a+y`dOJgC>2ir{#E&3C3h(ncrK}i;NhqSBW7!c@C;JYu+pyp^((Eb>`Z@Hp z$B~}67u;We&8kSUxV=cNMr)2i$?3h^(DunRdk+EWW11rjixYkQGD1!;M&@|8So(?4N z&cfrz<63QIv7NSx<;wIh!b*+7TeK_WaVg1^OU8C#?m>LS8CN7%k#(dP5Pk zTiehDuO`64_!v3NRyR|Tx)i2iJ>-XmNj1K>I&d!>Ca6ZlG-W~SalDj{y<_a!Xs=3s z^|yGi1r!^}W0N%AK*R;&Fui`suI)ptlPdm?l@(0E#IlThq0`nj^#jtH9D@s>&0$%rdve!KFS1MJclhA&wX9I|7ES<;hNy0HQ z{-W1%XB7<204YExCyTaZ8Uso8X&sG-jZcXq_M;Nr3p)^q4m{!(aOjLC2NPY&k0v$@%0wod%VskA+@T!^ zr?(z5zSMYLv7>)FcIFb)@t zWz+X<|4zCRYXrI-t8Z0rHr6wVu$a$cU{OT|rv=UG!UQ&>RfWwlVAdFR9@(G=`mI-F zJ3PKo#)2JNMR;&w+bc4Tdj$5qx$H8fwGU)xkwxhK^Ld~cA=D%uQI3hEkE$`rnWP_o+=2D4sj^nkE07=WcXr-e+) z5z479y&Ep=_&5#d8DXdOi;wt{GpOcMk9p6_Y~SqsuB ztVwDPwiHX@DP~4^BpWcaTqBX$p#(0=vap&t7XMn+#p$Q-(SF$vZyC;>)hY7a<(vH1 zP+FaN)Tae*2I6HsWS0}d)(`#)dps+>gXYj$AxD1-4Mp~FXaT;^?HgeC-!zMLc_{CH zt#^)b=-trgpHad&yh_TESMo~aQ4PZNwFABWsr7h1qrb`+^>uOJC~0Ap?&o$UqaL0H zUPTx#)eb4VAsgb`*Ab%lr=mt#iU&ML_apJv1s z3iC80;_*+6NCcl4aTZ4GZDhpfN8TiEYRrhUkr8J$V?=93j0nN{vWvYgN$Dd&TEvq* z@l>_VPI%S$%VNz&d7o)cdWyX1*a#eLZ(@Js5Fv*TZ3fFMC~VR&)Upy~YEqJktADKI z*o~u-IBryuW&GrlV;7D}qVKCDM#;$~&7?HXg-T*!t0a%QWIwG&&y`7FJP1BhJ~w}9 zIw3W1H6T{)gS|f~?O0DR1epci4s`RrR%2%UZpS5Z>8-`3XH+(aF5=3}dEJsnX=y`% zVEi_%BRG9sMrXMT`!UyRj0;HB^*|Gmk?w3=F@AC}ih2x8*Emv6K9+btE83d&g9S19 z8!Y@pMMMpC1?NUp|RnMV4b_ z#U>3N_~S>)S+}*(Raewn4#7}bSqIQP>w4s2mc^tVW>u0&JZql91V&t>n)z@s1B@X< zMC0xZFzU>f2!=-@XN&;pMFfNX08pN7f#5!Y*A9qREX8FF%An(rV`!m4$`gz&R zE3%nbZtg&aXxE4eV`(MYnBW#tr@#OKK)ZoAg(-v0I;Gw+M!ZFYi-^4(xHwiK_6qmd z7D4jJ!B<3>OW4ndb8z>v@|n_uPOLuD25?m?IzS>z#-S!nG_oR$v5N3WTpNd)v^3Q6 zY*`UGO4S_;A7Ft?Q_m=fa1#eo176 zrS<3sWt;qM#k_^m?%k%;Ja?DJG_R}St^7{e^5pFrXn8P=GjSVxXP|M~l+?L_d_`v1z6thaR0Nqu}Roj|+0ipMPWAg#}6 zLjb`mm|dW{)geoSRFbZiVoqz|tL(E*8q04%Y0>#PR$Hs^7NH-2jf9lDi3bZgp@PT0uTI zoxf**^j075hU!7|lIo}vV)`>gPTqRuLP~25P8a#l2ftGDEH-MMr>bYq_WHr&l>WUrXjg zq)2w~s=mfo?MYpZU^v$X7#wJE7g9YibNcsVU4=)#Z#@ey3J>uxw*xsn%jtg-C&bS; z^Om_8B=T4{%`TAML?@$n9><^=3FiEVSokxs5WG!)rc-O6JuEzf2ndsn77(u5a=GC% zyb3nMaN>6jqCymZfufe2a2*nH!bz00<%YvFlLxJH7~8^DFN=>LJbG0sQ1rcZtg!js zIuw`hO@&b(rV2+FTx-Gw*G?`tS#n5H?(;)t^NR^m-(ntP&l+QEK=f!@FIv%Y*b3Ka z-EW24^hoF=%;ZrNCQ7avmWnv?|D~oSy+NM{Hc^=9K|n!I?(~umReM(UrNBk#7ow{d z(JWM1gyi2rv(Zaj2S9`|nOl%sW=2B*rI;v&?rZ?#_ zf-74wT7;N^GSRRrq>s0ShIkpycb@wT%~>%r{US*>r8VLhT&MztRY@Zd$?KL1VF;s1 zOo5ve)`Z1|7OZUzH5gxE&r`P8{htk+w?NM6Ofh7P2>q!4e)5srDUohyJcwle%Q zZ*wY8>{4xY#Lt)zjvZkG5yhB9v*dU{3}EXnt+X$|;I=16x{b;);(<0uR3C$!@nf7@ z8aPi=fWfgv!+_f~g|X!rzAHqpD+yl{o=$r<|MFZjMfF zp`6MDw?x|nB2mP4M==So$x)DKyG2PeR*J9=q4bPtTt^Y0t)VvO@5w1 z#9_z=xjGOxLve8(I}(~l>91n4@A$UkEy7z^NF+`-&$CU^&GSxGsXgRBnzqHS1@^J@@ zOPDjFrLJk-99h&%CYBMLLah=}Z-ClkKo;nu2nj=Nrzm3>PGS}v+hRPJ9pkUMA|72g zvZE8#C8XHuJv1ozvJ}QZ{3I7##YvQykV7&#CvqmZQ_ky2Ib;``3Y3j3N^w=q^l7+8 z?2J^1?CiQ9Y3A+ZXRGbXq6kWY$Tr|H)wE<1jIb(JqiRw~#pV)QI#30J(FJak*PwUI z4-eZ=ElEB?&R4{3+7E4LU5k3iPWV85037UtsDAIy zY6P?b$-vYmCc2vN?oBXB2a2(9gACoWro|vk@z_Dw2tMJrRp|9-!`ydd;&YL`{8f10 z)hkW}!e=6%{fvV7!x{T8oM{(k2 z+E8cTJ3RE$@LW75dNLXhZqBDqjQDgD)IV<>-B3Q+s@!AU?{2!;WBKjTSzh2QxAhxB zVLi;_ek!4>X$b0czigrdNMWNuZfm4tfXwdzL&1A|A&drXo-mFE&dRpSE}|rwj!Yv> ze;eogkR31g-J}H1GWaOLbAazfC3sG1f@GLon4KH;}7oNqm zk}j;1@YuZ+ZsX;#edxKd9*V^s_n~2TV?6l^7|R*07X7mf?_y8J&j*_P=XK8W&+LkM zJT0x`(OL_T#W@I3kVe|V)#EkOJltjwT8-xf=Hc1k zJP*w2ojll@T|kUI^MK^>Ja1`jRKpKi!p_~NZ7l{c#&L1x4TFQy>lPjhX)_+ClE-FiFyM8H5Vt22- zDI`HQ6CG8zW7g$7*YTyS|Jb_VEDrl;V*oYwWOT%#$3HM4%R4#Fa{H~a) zuz=yaWRH|cdlij~A)i}Z1_WCcY-vVs&{xYGBF!Lg*!x}occnEL=%^?dR2E(R-j~t^ zRm+Lq)JTNNTP-g5g34EfN~p~wR1wH@9Zq^g0v-R=ia>EZp^WIkujlY0Qfbf`o0G`V z-#`xI;`xdkI6x=J0pkB)a?D%Y7336=jU0&WFO?kKL}hx`e;zJ`#KfpViF0)&8neTG zZc)tJ|1q3DP~s%|efT#M{SvuEKiJ2<_y_j61IoZ3Hl+QZ%Bz-7g7m;9Bk}nmy`z;E zm5(})+O^F4Em~>RE1TISjrwmpkew}S8ns_gv(t@r=c_xylU-$B{~?A$^uM8(_XA_*9x#3Dww1lUFQt^1bB|+Frpx z6^p;(Ss*yu0}igL1NO@x>9+~s7p)qG#|1nrBnuGXZ1@N_BX5hmz0%$k0k&js=ks<@ zIPS^b15S`cE9OhO?(t??TSJy6Q)Jm502!4D><=rm!ii|8<5 zh|jtIv(#pTMhni~lG1f8C3$!>8%EIw`zsPH56}FFrtWKbi=yuh5kUC(zfX3aSWI=U zn<|oOKl0(K_8&6cW7CgFNEn^+@Hea>PZ%LlidYX4w`rpgl;H#yf95SGs;+;ZG5N70 zLi#ElF}uw6V^STN#8chGpRI1vl)10FKi+ViV=k%Zow1G0ZQuGJ$2Qa$8_*oP!BA&Qdl3)NOE1U)eYB0;FrH0tB(e^NR z>uK_$bfLehDWw37EebXcR5uqued+QT0p4bSjX3UDjlN`!K(v|&B@R(G`R^3C!8c^ zlWU%IzgE0?$gvj_102GETf&8hPQ1&{A7}HbFKtw9WFoGss9^yIvqe46w55zr2|Kb0 zrvnIfl0!1s)E)eidNW?>PF*^PNnu?C6$UXp^KX+;XhiJ|W+UAv|Mk7ub}P$Wk=5!A zn378%68m%hUs_KmJO&h5)*aq=0^V**f@cL>VX2|Tm=f30=Rruz0uQz_JrA7ji#(WT zdk3?oWk75WTvinde$B`?!9`6J?5Y`Rro7mHx|%R?Af%lh1}enl{}nipavN!&>bXdB=X- zDaXJKk362kqnl?vW?A@KVZIHu9?69Yv2|6S5qsjFvh|K5f02%yHM5_0YGx-eHP&l z3p1D#aZLxZSxd6}H%8G;2Z%XPK|G*SWiby)?QKZabU<6#kjmw_q|R?hl{+{;{LO&_ zDFodU{Hve86~1^RN*A*8vd5oVQqYF84(!SP>Zv7q{Bggq5_YypKVmn#}_yIHt zK=Us24C)9VCAEeOdblZZz_zV4mcpom^d03j`z$l~ZmS zIoLw`vE%I`1+G2kc9Dz05H#K?+hGS9s|Vw_yrCx9B5u1$Iwz(>U?6RqN;nSdAe$&? zw`*a1e5qSYKq54jy|cJ?z~&PB1fnI}WP1m8ydu%kr`9x2z7$tK(s86zY4O_V`kzlK z<7i{-OWQp7uXWeJI-zS)2ZQ+gTSn*Y@U1C?75VG)72EzQG^G+R$RgQ;;@wzh8IS{$ z=~t{E$r&E@bGiA7QC1<;#?JCv56K3W{zysaNDsTq+BrrN zscVV+Dox0KflSe@2y7W)A+`D2;etxN#w+ zacw7+n_Q6*ZpDE}6*d_Z7aHG^^*7a=HFy^dVzFeIW^9wFOaq2737?0hudF~|ep<#} z-7^apw!(LVLtJ`Mqav+ja&~02O?FfSL{VetRfx;TCUWs3$ANkj%F>_FsDXE?7Gdk= zvoTN|JB>xxsTW@vVyS10&xzx{b9}$4t7xsV3(UkYT4-FOV+YZQYLJ*p>pe`DYr)mX zYiCki1nVgdNpq*tfYA)`MM+diSag51o_8eQ_nMVK?Omvu%BRBzvU7QOyy^irKd;nS z1SAh1zc{w6#9kbJV*&hIb_$5O6Msh1oG+x>-3IJk%2>)+awm30+4p8f4CswpA}@K2 zwZPy&^I8a5arM3{EokzSC{a!zW~EhNMP$sS`VQ!%Nd6z6+un4lU+KM1>wVr_iy}MPqcB%42k-PBY(|($>upr;P}y_I7c(k&zEu-bKL_ zVlvQXuv9sV&!Xo-8YE|nJae!pCyQ3i^TBpK)?&$O&h2`{h@g;AjQ{m(8#?GrTOFxD z#Ei%~P6QdlodAl{f7LdP5Z$O_6+m7MAknQjNdyo`I*N{lGf(sS4H}AQ_{vQwFC)?@ zub-Os+`bpJN~0>$&&cJCwk!Z6D1d^mMubEMGlh!@3RxgQfy-$ZquKqwI>s<*E^?ar z*2YXX`R4Q4E;6vdo6u~vJ7RsgO97ZdjkAy|)Y#47U+|d)OvWM%$HpS<$s&qA=9n^y znL?Wls)C86`#KcGab*|ba}sb2UzIrm-l8)1R#|0)rd4L!Y@)KoLAx2WhUv=0PdBN| z-oI7W4v$*aeJSIXTd`&KL&lZqJON@GdxkMHE|Pl2F5jqU(iBeFGlHq6E7LR39rdjC zTs_04nXXLFG;gb(70=Z(H`%j9Q?Xt}Q~2Z5G-Y0#)G|~UwVXdk%eeWGB6D4ftsNop zcQr2OLVB(%J`YBf-H1q9wlmQMZfKb%N?x>6+AoVAAN*zcvzV^RMZ`+L5hr{>d?W66 zZ+uJQR<&(12C^h^5#h`yn$mpQRo1pNOi=bxNN9mKeQHySiDbb@#EK-@s@wmMR^0~6A_R%OHl%458uSc6YEtA zw^nu3;<(Hgh+sslm!kgbScGF z#Td?#s`KI(lp7!4|5lvj*KTIh7FK%$yKOEskJ z3A$F=?l0H4oIq2shH0o7ie97%Y4#4w$i^r~>C`j1_(t9b1`IOFD44WYWW%qNcd1MaH=&MWL;}5Q*2|@H{0Pm?!NCKF2{QIvw#1Vp`2Yu4w<4wiK!e83?1Qi zgk-tpM)#5*?SS9ACNJ$YsUcR-3aEB`w9n*f^K?`aO?nBuVj;g)mNd@PkRol=bOY1y zA?LUTABU=Kw&(%6y?+UWkAm?zp2_2?@y zwM@V305Td_> zD^9de&5bNjsg6iT>fH~UbJ{RRE7FUap3`tz6r=|hn{dpnI4pCF zC3CN(425^{yE@Y34b9wRq3=jsmt6CKlu;|`L7zAr;IJQr( zg84yGBsYP%={6(0iSa5Gd!VgTpJe3j49CUt&Z&JjiF z5dF9-&Xr#r7tLo8ZnN`Qe$s!1)^TlzfiVZFShMQ=t=n-3G0@zJV1vpTk%q3=4^RKx zdKO*~)_W-1z|#UKBn`dc597rYx-KKtcKtJJ{jz-xP&pdd3I^&eCY*fAU`H zXp5H5lj_{}AD_EUI4%zPWz~j6I6IhRMY!)rNfZ;sA^ZR&m4r(q^^Ba9$ABwmzavva z$&awwOB^6ZE{%@rXy9=FS3@ZFVaUve0H!ekAmZqY`EZKBeCm&_mmjFb%ikFbZuXX> zm1Z|67;%gshSv@R)os``rQq4JAr~P^6%#z2*i!b!Kk}tLh9r~>PJTUpLI^c$81DJZ zI(6~v>`>-9zBAnc!lXt40;1f1)5Xk1CxCL!<;BE*F1juzVOE9Bu~wsOT?TWPC|S$2Oe@x-Zxux?X9V3f=T}zXRuVFAraD2cu3`+Gqp4N=coeJ=oTj; zPSI^iZ5XP-miPEM@NpzCd z^U^!DuINfnUCwQ?e}jw{7GP zD36hanAa-DYRr*&>mV^}WkWwci%e+Ti;67YDuvW}Ga38Gd~sgh$013r3rcq90zt?y zte$m=dwQtb{UY)_P%F`)nSLCI*F?7KT{6!0^U*qr>TNDlv9y!O(}8Y*B-cDGb+v%% zY=>59!kS`!2q7lc*l39-Ukf`SYby|y79-Z6s%TOkxDmNEeM#L%{@KhQFbsRL*BL3o zM}K@>cM7;cv~Oe&(ZZs8T}29u(_VCM(4mTrJv~MLA`NsdH#S={kt1Opr48Ekf6ceU z$fj)r6_woNV^Ya0eLR*^zaKRl?k8$_uW}z`TKXiBp8WeIiMntB_KkQ$?|UV0^`^&S ze0b6s8EqT#54wQwKad+?0MgQNJCYB|Yp2!0q!J0|TLPF9a|Va=I9LAIMYo7*$72_& z2@gMLhiATH9ro(~T8?(O_MO!`LNE>S!!DS=NgUJ)505MisuQ6(4;f?&(&o7-*4aey zE)wnonlza#JWrldA*ws+=ooK`b>=c&_2}+*kCu<+JJ#0L`l11iF?GV4>tDdBobUkV z7fwp0-_L8uUPY!4(d%#@Npa4G5Aez?rl9!j9_7B5Bu;Ou?%llF%~I%MTuP=~P<)6d zj{4gE;-jOtb>#{FJN5EG>e=MT!~1w;>CVz14qN~*zuk-*!Bz%4m5y$}xLTVGkoHeY z<_4p)A>D{;$Iod?mf3r1@;)n~(qkPdcOi z*>V0|`-{sdz=+-R&h=EZe}x2Mx>5cbDTJf-)C-a(P~qWA>joK2X%qsDe+p?%5lg16|qzRAE?|&b&f^G~jJl&FB#nh;Q zwbmGqliJ`0S$b5p0ZouuK=on`+hy5OWEuzY5xQoyVQ54efuXuvcGW|Y3cl6-p@OS) zZO{;%p1)X~ni9u`ifncG2^c?bcP|(6E$DJ`(oL_S4Vqo?xbHJWD&FYF*MiRLLMTC- zx`47IV5K+Y9=k$?QEs^Lzlyb$^zwFhc5IM5-Kh+FbL5VT5NT5W1p1n-9FiMHtiUoU zHy>s)`ZsT7VevEc%^6I7Dq_Gliy2(1LNdf@=Sj?L$cNueK;Qm zGL@Vc*)C>g;%urDmX9N@tgz$_&-cO>TI#HqqI? z1dUy5Mby|^QDfixkE5}p=Cl7zAi`+1`ocAK%oGt#-hMPG2#`9=>WIS5K*=y+c9>~m z%CIebMq&*v|5rMK>CKweGA*d4xN<%)EsFE9TweWHi>W1bAe*#I)R5LGng3=PXi#nE z$kcHOC=-%t)9&{rDAQVL!zsN@eV{8G_(*H^8g^7QP2Cbv&46SLkIVlunxO3^fQ*R1 zgMEInniq4TvhvSPeuuf%|MeWnN9caehagHRlyKe447MaD2y~14daRj_b`Fywqegzh z&EP7NABGyirsV`wjyd)BU|I*6>{|AUaOZ8a*j?-;AcZLx)E`?CZOH+lon@o{c6bun$J@S*tz1P=xXcKsBa}Lx zKma1QneUwfkzR9)3OpDzLcXDWDb0^l(0QN~e*Jp4%WIuS&mHvGZf;gQZSDacT`J7}x3Ihg5V= zwxsi%5-@0!OZ$t3@WAnPrc3PEgL;3Ie@soZLT=~&Vk!K^x0Auq5;YZ`(#P>TN2RQN z@ag}I)CSoAO6h-3u3<0_K}1*si9w=NG$$?X714ItAj?>3NDa;=bYQn+o4C?~;gCvk zrGt!z`pI}BG|X%Q$XE-uiQD~9fr6V8k7ok~vm>`<+ZHQvYXr_jIv$c*0K@Qa9`2G0 zjHf~ zZ^!QSavip>4oGRLv+SUr+^G}YOtw{{2X}`)`|PhBLz#j~G*E=?N5kDvS}}4ZXQOmU zz3z|-YsU`5E`|B-8b`y@VLOdF*})*4%&RyZv|iiMtf&gXB3z@JqYo2@Y-M6l7;PGT z8hRw@+vu;x>iF$QWu`INP0B(^x?x~tBV!QB26ZM%auLLAQd!UHi)bWk)kM@NC#6@` zYWIhW=*#!EWX6FrS~yY6_?hK?azi-@SZydn%bf8)KH035tYc2Kpt5Yu^`#hA|8U-(18FhRl5ZIP|F_pVeiwKW)>GqcS}i;5Yw=sW zD*4n}kQUCdw*I{Crg0&Yw})h2=p8^u7})aC|B?7}Apl}gMDn6;oQEEvje2w`{jM!V zw)1JoS2oWvvy;>TSdHM9VgG9}mUQ$kR9R%gc+gTh2Wy^iG%X$>nVGkd5{j_MFb$(6 zrK(P3Q4u68SCLzOGPGhgqiT*PTdE>>I~9O1HAJ=LiKUlyziT{jw%Cn_PLmZ2Msixy z76FKApMD^K*K~%YfJX1K||3%kaL-BGxR z$}+`~2BT|y6zo7!!EJtagAc)z5gy)jRYD%i+kk+nOTa*r6a}ezAfVvYf^#u44QiKI z)U?^EXf+xr-Af9hlJv;YN%>K*EU~SxQv*nG05$9|{~5Y$nM1l`C1*6#xskhuN^G8S9dz6=bteL+q-51QkdXgyBKEnlwl>Hb$x- zwr05dPo*d?up-2BwBh?F65zUKcyCH*D(c5t*vZf%4Oni-=A?7@dt;9#?j&t~R$sdB zw2BGjItY0xxh~0|y^c5ifXP7ss+ukf)th|am)o0EbY>E1@{nJ^)9w$Ao2r_Na=1xE zh*zPIB_ZhK*sGG_<=2Iem4yptjEZsyeT3!7_jpN`u&(w z;r?%uHFlT$C=8i2w*F6QXyYj;y_qu%>48k^+bxW+jVW3tY)Iu7mqtn{H>FPH;mMoG zyI?9$zXnq&v$-633}SURvc_~GGu{n5%DiSO^U>*=Zq`JRIjL?Fq!zY-PFy*RE7;+Xi`38hk_$}yy2rl8ZL**vF)Y?p0f+->HbiwF z8UR%R{~CuByk@aerCaA-Ej!QCF%^qmarKfq#C6meq+saiF+UPTRk|%TEd3f#cv$#I z%M%49^lX*mh~g2gh3Pj3m|6Lu?$ZwE1h{lp!6X~FFc=j4#jod<3jVq!eiOLhz@S`> zG_)F?;pnQ^gDUbLUIxu{-6dtDfW^oHYH0WOS(1bvJ%e%J3f(Tr1A?QT$eC<8YDIsb zx7S#p7_Wl#sG-wz<0^S_YMcd%pQzJYTXYBEeL6_1Q|k=R+TSM3s8AO;={!-a#*x%I zJ=p`SrtA=?S>tS@;}0B)??AyYr6m$Sp>z3~0x*Moj9M$OjkTStUHi5FPOI>nJhA$b zH3&T6GKC(BI?4~>0n7mvK3F0Aw~qU%QpfZ7+nC1lfU+HRI)Ixto;QsQtrPsQTUzKD zG*QorvB*mC672@8ZgC45bbwWw&BdH!xfh?r3Jm5KQ~@O_R=}hB1y#^3G~mrcR9tEdcR3ke%*k$FIT?aD##LxM|a6iSPUe?;}=J%tcXg_KP>!j2w(r}dS(o@2< zuAOMMd<3l@gArSMGJhargUfgJYxSeDexppASvm-Xygvux=_B>XzH!r{@mGCO0XF}` zBeBc8pC9QVtAnoT=NwVx6#-HY%qg=y-czuNfrh2QGUG%3y(cVEK~68Z$KVQi9Z}iB z{k9P%-Eg;d^%31dKf0HqSI)6oYwwN?a5>GDbrA^{@Z6iO@Y4A)ZZt#uvrjw7HATIx zS+KSFz(73yIwJYVDLZSxRJGg3WT}-}@?vbSWNU+@d;bz;ux>&Hzus zB>hW;DRF0XWR`3~tcNFwg=0~~f-pvjidb>SQWObmzl5Y!NZs)w1_a#0jnMeBMIWOP zb38@P>j$r32?f}rOR;h&piRsxTjTBm#oUsanEkyXE&-9adE(o9i~f7^Xs%ju*fx4x z$j)*2N~p7AAcG7+F|m)pXXP=M$c-2n2zj~1{Ls33GX))4Zdc6|+8Vy=7nb;O7H#lq z?5y?;!mLJ_6kik6DY<{GySbzVeh^I^-cTc{nGWG;!%*GfK#cezHf_Q%vIpE}8rJ7! z4=A71u1ppt%UlueDWooa$}tK;BqC#yC>nmYXh>=^QxN|`69!23gpM8}%}0Bt|~22G}K24E5?WQ?1i(W&%XUJz8C z624aAv;sy41a@4e9~Naz)WREM&0|%j2*iqhzIdq+k&BE`3eV$Bv3Z&`2$Nd)z|SH# z-Y*T!7W^8(Y$-Be(g96Gb$?HasPQr!%gl^XlkjqNxUBLf$>(U^B)7qvWEqg@IlW0W z^Cs!i$Ya1hqxbNv`HkfIN^`^PR` ze~Pzt!f6MSuf?l%H3&U@awUxBHg9W&z6@aP6y%3>|B$}8Q|M8Bv99vPwn3H6kCni_ z@WW&s?k<}yH3BlKW+k>B<@ck*lLi=1xA+A#n7wSX=uvIrR@(?-JCOojGhbC^WtqfU zAdJ_>cyll#;;iJob6Y%SLWGO1%2Y8OS@a7j&M+55!*Hd5nXh-j^}$rz=CQL~aC|3Hy( zcnm%XrRNqIfAqf%VULb`ZaS0}BC+W|5@C;w!GC_jc0cP}GpcEENu?tzAJnJ3y^tu+3 zMaR~6d$3~JJ{^*VK7p-GZFG?sn^B<1*d30zF71@Ds_NJ=4}}V`#n%f@-(hl7XoTa6 z>AjnOVodMH*ZABM9;bwzx{4R zgNJjX=a7Q}cl}=WdYK+ZSEZZE&CgnB!$=!Kc}hnG9kr|9yWbo>P=Hep{IJRf7-;@exJsW-jj-!TBZ~FK{*7wV z6OTZ`3c@g)!!}vpi(@H-$D`K$T^V*PfNN|}OlWR2j^N&j>0>`E%CKv%GJ83W*c?se zO55?Zi8uhhSvaVkoNZ#OWpDTd)aJTKme3A|Olv}i7G(*M-oHY0<41NOm`n|!&t?Go z&}#&PNR!ILUq+{mfh12o5Z*B|mWjGqqx=7f+SGyt4)PC~8+45w3s@x?8Dxz=eR z#))F_5CbV90~~J%(n;|Grkx^&Jc_CoCc7I>?HrcNOuwNM?KwgUG;-v~74S;5Dcxfw5NW{|aZIB94ki zA@z73&6MH$Kz96 z?#(@Ekr5-m{d3V@ZK3^~J zCIE7m_5J_%J2QK4k_m=NhT18Nk}-vYct1pk|mp+b!V3lEwVtY zwxW1or7Cz7@uD8ps#U77Dr&v3MMdlV(`x^cw&-4Ax&TbM22<`8G1fHGm z@tmLM^L*~-`6^7o8uX>W#WYKl6M+%TP^xa0Zw!b>6+dbqE=@9kxZrJ|$5$tOrvlVT zZ9TxoPTLmYQ&H*#L39|_yYE9hG57~5Kbj$b|aWBW2`h22Mhl& z{WB}pK%a^!p_!n^@&p;^(t-i+VL8hY60CKHwG3li{uDYDk zjUoSwOd+uzZQmBgVAks9#-2P!J;vR{1G$^Y*uTpBa2_;EfsyL_M*aNqeIuRfVBDWD zE~8EoX>IL$5>{6-W|>FnyNIQ_0_y|8!4y@?;m34UA2+fM5Q!}7nahLTFjAY{o?;{r zOkSo@J!0};V(_#c8Zcj;foJ)3+wB--CCV&C-ENpGIhzPjq$$Fg!<|Xd^r?~%z9yPHc~P|g%Inu zvz~iBp{KG3vMa>6>!}$J3QJ2Rv@m?Xy~0sCob-0>QQlU#Bckn{lvnwbn5Xc2>6>>?&*> z>=G;Sp-U1LA?LQkht752LpS~s`WxibWHSKkREG)(Cj#zqPl&Tuc`N-dddLM07>ad< zndoJi1r1mw{rzgDWF&fB|7NBH`rTmAPoz>&weoH#P=Xlh==Cf6&1`@@!hwECe9~N}wZZs>E3Ueq^+9g(A&SvqMXO2t}9LSA}i;AghfzkA?Xx zJr@v-|T_{cMZkNm=3fGDd?_(*L&K6bb9 zk&F+6k4);(@lmn+`b=IgKU(F2eMEh#UKL)}nt@f|nQpQN91Gmizd%`}jgyqR>xw^#28NJ_71>B-qX=b%C zDMz(yO1cW{nlR5W^qXqr(!e?i2KGc3+VVQe6BSDYo4Vv#nPZI99R*2SSS7}Eih={X zTkDwf+E6{X8Fun^yW#il%^Si9X-B=r}dLjvhUyArPa z3U2w}yb6G^84pE`bPeDzI~f5D0?Ek*9JC`~f$=`DLM5YtMFw6a3B#%gVw^rjkA=u? zXe^MDcn2D3B7_N$7x;h)CD2Jdma1v?S9QS|2~ano1W+frjPzAtBh$+g#T`Z=^D$5j zPyqx)mkD|7Lot*SU!CLX9bR>wg!^`sFSg=!5m6bkm=&&=pQ}dUuwUJmY5-2?( zLVFM)V1;(0>vT%wM+)sGBBnFTV-(t1A6jKhlN)`;O06V=67Ik&e5-I^8;ZF`@yP#*CLHQ=#?txe;~fCUYrL zLVL2wYddSWV>@emGpocRsJ+kux-F!4V}_D;Zeq2TMvVdolQ!$#@&cw^gbG`VL4lX? zVD6bNuMB|;bO~~bQloP=plz<{C6VgdyUHuHHWMvk^3BX>7n-@e|AQ^;V;?%#FKM3r z_1Ac&h?e%b9^ZTM)HS8=VMUmI^!m5*==IAwAARe4JX+ZKgfBDP`H+qsKaF}+dbl=OTuPJp0L%5goKpwhzYKw_b zHfDjZ{DcxirMrL1&+9{_PyUSGcZH(o4ZKA9M??qRU?jlkZBPgxt_mBg2DdTs0mOum zez5!K2OE+2s(}3YX2nG23+}kC00Mnki+yZ`!&9S)ga)6$Y5;H#$O5xwGUH z@a8mgF`^+8b|bRn9!_jy5TYsr&`x92^BJyPTBX}xN_S;xw8&ymUpo!}ig=3xEwJC+ zp|5Mq zVRK4%-4@)_h}x1*fwn%Vw$y8mMEJCA%f5I}FSN?(#P{2snA6XnD~p&pS^Tm-S-@iJ z^4zdjci;sfYw)m$F*|giXon6k*N}ZSMw$KzGj6$6JYFqe(j21g5E7pO6IV7kSm)Pe64U)~sJ!g#)?I zyfcm*{~a)y|9VA7Q9wQawLLDtNDh*#J6_Xl0u2y-3LUmv_6OTJpGg860)mV@GUM4+kR4?0hFKWCBiYR{N6_fCANFiS8#8iQ z~N8+z#|l-)4E zLlDE;drSZI*27NeTwdOepnLl#xcM}H`~xdWxAa~EF*Sv$>X7Kvk9nP=v$}kfE?=Ze zMpOE^3Mvvl@Q>OGZFs{AeY9Zg5vdz%oqIWj zO{TtX5Cff$TNn`*fz_e)GS1K}tNj7a8Pn%9dPM2<6=n8<2tWcA@y>1X3HFM7MtpCK z&YVq&V1c^poMC<JxV(59%)(VEm#b?|RFUY9GDj_Ily7DyBk zVi5M%K47@r75jZ-bp@4OEk*TpF7rJb!32HCx zRMU=(tWYoGobj)uqZ=1B6&XVu&xrtcbp|s~3P=Bq8h{B6(?rOaZs@eStj5$2_CSkI zNcPlL{uAC1`;DE5ybuhjpOj|1q%TISbyfsh zxhNsS-1on8n5n0}j^0VFHZNcO%_Co~WG=N!n$bA}KX4t>FT?h1kcjThGtqnSO#o%} z_#NjC&OnE??PPWp8rsG)8Dz+*zC$m6uWUv_3d&&rYOh?sjKM5AOd+!*wNibi06;|p zX>R@-EnD-(z+5ijpv?R?y8r*mFn>6Uyj@H$9QK&V%R3=2b_IjHhLA(r+Q^ z<+q=>1%WfzoM9FvK(XOH`d*MEbYj}Ztb1Nd7a7;`DF7!fNQBP;t+YYIBe{@V5FoN4 zEM`T_M)#N~7*WD`(LFqoP(JYBA2P7P_Q1aE9M8xG|2s$ao!ODOt05{(8S5Iyh0oGn z#9{OyO?*#^ATH#)*S!Nze^0pg)u{zS-Hili3lB@IO;G8=1uuGk4!|gZ)--U|S&) zgzcffrBI*lRL|s0HP`-@LT!6W1<2nPGE6oXUmWRsg(0qTG-Ck|MX}MY7jUj z{@*d0eUIyC{@%6m!;gZzV@oUv&rrHd%%;UNt)WmG&%h&VUom)wmKfj}XevAdn=PI_ zo)rCmb}R=USMKybJC=Qq>sY=lSxH2U1$cz?D{P)8`=b4l)9@Rjc4lQ?!{9+Ai$4;1 zgKunsd-f&}c@Z5@`Z)vKc!rHA02!Pw+QNj4Xgg0FRy**hIk{zJu+MxUnQ)W;e^^Y0 z?9=~Gn5{3)3Pm!+Sv&F{X)gnZF^pqgK|JlYD2i+z9wW>Ttm^i)DvP~#ocK10oWT;f zFcc1f7C}`Cf5sf;dlRjJ$S_($CQ6@s?O}sZ3e{PG!GRNf5jaX8e4TF`Dus=MA=W)< zTEx;wq!m#f+f8JSkD%6&?{n3E5c*kWCOFh2$u^CSwDsXt@SJvK+m+gvgt+X+tQN_&G(NmvG!#3u=Fjf zLv!7Jr_bH_I`TLDNy1v{7%#LN06v7QI28JzzOWUJgfNnn-g_6V43*ycJ|L*mi!?2) z+>&bnh%|dm>BlnTEsR>yLwWo0Bc{n!PkoCbqu_?mqdX)EKb z5Sqcu@*z&hG+K6hx3+bpB8+kN4Mc)@45=r^8j@3oMK8?o+=2H>zFr znA~ceR%N=5(W`v}UsIRCGUNkamr&nDX5VMD2%XSRZG5pQ6n4XmTHih6l|bsyvZ0ye zFoR z7z^?sb0}8(t@mlkNm}vaE4ae!`Jl?k9{V@`0y^xpP1B5-v-gbEh<@U9GoCR;!qMx3 z!fQ>CkB)1QA+vo?U`~jj@%H_Y?qjp}cGR}^*U&rVcH5D$QS#(*@8O&EWwIHk0 zP|Pt7hK|iWcVm#F9Dt#MW{8&266TIm z#xy`m`vEgzHD;+YJRcNLNY9X}t6)C;CZ1Ub@fz(zXFT*0iub5bvm4w&TCi@l^Cf zc6wPv)FGa6!K4`*i7syOzv{S>$2i6KTq6b-SthvD6W$w>T$nrhftwO4$V0uOS{@lg zyuo%c?TA9tI|HT+PymZD(Fp0=wSr-On;M>OBty_HCjVdk8N^iIK77Axg-&vZxUUN;|d$LJjUxBmxv_nP^=1HFzm zuX(0s9_YdlETDCi&E=2Nw7yw=GvwXK1fVgP50cmoF3v?Jel2X4jd>*e(ZXPn$TjGy zXrFZrx;kFds5_t(oraFKU4!k7bUM-zkynBnvMUOpJZLtn?0>h^y$3f<#{1q5i-1jk;wwWtQbj9 zfIx3GEM=A*1bpB{ulD)Ctpaq2I>ggVNF7iW^e_|{I&OFfl`-L=Fe9{wm39Ge$PmJu zEjxQ-w%Ww_gh7zOFRKL&vo%#ilwq3+(7_m?y+N8OQ}Momx4CUKxgCEZ$eMzkJA2LW zq%Hj5=EDm6f+H*P@32FI2yiop7~v?IEaa3!h`B2*u%S}Y7ft(C?YKj)7SAb;w9qaN zPPnCaD$hbwoWe#lfcCsZVome~A$6{FLA11r(fM(V7AIn$5K0+ezy_WI@KER*zg}+v zizw+>r<`09iX$@6Rf%x)Ru}MbVYR!`1^V#1iO~RnHhYX5Kwq5$kQ&m!tAvae4TZTh zs2~5sL*#2as!LY8;1Qunp|+AiO=8Mu7veyYEQm+|Qo^f_C>+Vic6AVSqM#b%V)m;y z@8$;1ywVN6cx7b`nmtP3j0;$a$y1Dr5q@JK1+yr+kf$bahJ*GMI3vBuIDUYhSe}Uj zTC&QhFJZkhPo>XDg6@oGQ7{7)7>CrK`LxffV-~*@OkzaNH?SB&%&7z%z+C$A%*PyK zt1$Tl@U}O#*Oxc}5+V&j9op_>gv+)hsr%O(eOk`}Dw1bg+%&)iCurEnn=jBDS|gEh zBvEY-qALeKX!~GSO^yQJv6fK9WZzhC-O8 zSnG$rao8!H%j)Bs1@G4NH~3>>rf%^UVIqi&U2~1jTvcm!zqWrfdlZ+k)dpv7DWA@t zH#?`TvseLbeh4CyWWH==!!jTOOd;1s_>)nL2OQ9dox#rWbZ}rwbv(9?-!LO(<*tQ0UlQ)(KxTu<){*-L=P->io0u%||75)cjkKas;F< z1G5Z8l~EwlmCo$vI7qJS$z`gM5E>aoZdONvP^dn@{E>A3`zvK zk=I>b4;hwLdhm_v4Y=e-jaaT?OgiEZK>%6%;`%nZHdCW=HeOPW0?V`k9&`xmsP$(yzYW(cy!ALjoXZ=$v;e;U8y>`5bvEM2Zea|<))7~ z=m0BO)y;y{vWamEs)h`9&u`NmNM`*opQ$mojyT_crjJbdL{8eFnP@Xl&wsY-1LHK38 zgcW!}1mJRzkWwMO4r>;(!y#ZuQh^=51b*_hZt!$qj}{3?PvvTc3&}CUQ4>w5U_pPi ziG8nhS(Y|;JFn<~5lL-^pd{o%0;;WM-$2s>&Pp;RG3-@XuLZ|~wE^uRP+5mf(!8M@mx}I$jFb`!)1Kjc0UtJP{iCr`ie*YfQoDxL zz;ZvL`qd`OY{;_-Z^EX(8~k_02}&&@V;sY1;`F zaJ*;svJV;(?OJ8yW%>~Jpl1x!Zg`FLT5CS%bFk$`*8D!pbDq_10aG&d8qo*fh%g03{+F#=2Ip+YDZt)K}MFb*? zB%Cmg4I3;ZDrtow`6~=jeU?$HjiziS*{l?LWF*GpGPav#B{UG8$_V5Z@%7ojiXI2w5WJW8-(BHXEDNyA&3CmME>od3sbroS0 z5e(Ze2?TWs?Tj!U3tJ)x5`vQ=fZH`brs$h3t_7VDgd2@s)xb>etFam zC^oOPLQBrT=FTNxIk!2*iZA>b86?J_%z7v}(Oj?scow}<6@3}nQjbLjNL$k~^w>y3oQJjKqBIIc+(%MVNz z2AL!y`cNl?qQjBcYXTMoSWB9<@POAR?iEweN`wSB1CKDS*73kbsLhPQh^##dMr154 zaBZ~|HlJ!U5}Vr80Xy_S@!L8nLcJ2oio9%{KlRL~W!oDfx>0k6C6gsSnpPPpkg=X%aU)d#q-6AD~lw}Mq^&0Buhw|J7 zRD-Y3@ea`zVMD`|3)fBn7##gL5T9Lkv-r&f>Rd<(C?8Zm#6Ur1b-8TmW@gNhsU;oFL??xkTFmkx@o8BY zC`Tl9nI$jXgSVms1LskNKsD-C1ReULE$o$`8&>Cr$ZR)r*~hn8DOUEso?K5&UCB_{cX z8%xFn|Dqc|2Froz=_04cXr$EO@PX7zmlO9_diR5eIZoVlN-zH$7Xs~)=%+#h>#54F zR00&q9z>l%kA;1l>q*F=&;*W+Sxzzn$cT-O$7Y2-Vg+~c?QL7Z=f2ia!96@S6+ETg z1B3k~0>fk%r+IVCfz&v{#DZ^C_>^OiXNYB=2O%%C<)eWq{&pi+kRhgK1wHlmu3HZ4 ziX~*O+|ujF7A+oG{QG~QPR=j>Bc9sg=8EEqx;sko``FKQTnW7o_wry__N?VvWJN! z9Z0q@#u_Nhd`rwPJ%9|s%9AV`Um3{q$d4J8hFLOt#aivyFl*0vGi(jY$d1T9`a+G) zfYx@&wY|fRWTj51y3EP{{GG$BFmiU(WILBQRzv8;RBTT)#0MWykAh}Q-%792=U3dh z-IE9wlrqL@zf{^FUTD3D$dCMpEvn-(`qfbMj?hXcdOt-MK=4HKqxUf&AR^Md3ezOI zS?eAmD0OkkDhK)mRla24EK{p;;m)vTE9=oPQlG|aG}?hPj9GzduSZ!~<+h!Kv`9Tm zFtpT$M;#k@G9`>IWDfv&-QP3Qbdsii^=W&F)<1BD zib(ut7TnS|zIPa*A-W8D>XyE%n@Duo!08UBK`LAHeIZm0z$R5fRD_eoOE(Ohdc@$= z4vLep?L2XBSI`FocGDaKGwO;MCbIDp*Q{#RbZ7<+o7m_qjEFDrL_Y^EoMZr6GKO6@ zI%sA0=6Vdd9f$VA?KF{EUGm7SJ)*Ah54|ETI{J04KDu zM?`OP2ad>Trecw4-B5@~^htKPZ20E=cYO7xzx?NqAG*QB4{!L#fBO8V?)<-A@#i4| zuLq+3xT0TiS*OS)?FaAlAE=u;DTm>d?lFwbulINVaqFLd=nJ3u*dAN$gSUU+M}PI9 zSNzLY70^8p^#?+?5v`)j&B}fFuB@WShWCE+r}upDXRrLok8GUyhTnYq+dq5gBd>ks zr%kQE*4gm=Z++?oFT4K*-@4wm+x`yIQJY;nw;!)|2j)HbaA?3Z{H4!)?Y}C)xVpTv7CoRA1E+b4OfVnOeq+r(N~sPw*@}+?3Y0PU zVTLBM0R|#v=)dR>+<`%!lOYuSJk#k*tM#U1LmF* zzCnjlsE+n$PRw1>F;pIMyO5?z-?{m)h#5pqZBsD%=@7cS%9Xys(OCwy=e+8-jlGv1 z4meRp6a7pmdQwoF{13S*PCy?LAD;tQ*754k%qyq#(A|E`rk%rc?t*ZX0oxG3I8&Eu z=)VxyG}fr~F!;ShE;PL~t+;^7k;(l9m+}qXM?~RU6d3(IcSo2zMvN6^?5IeBf8;{j0W3}nA%GBuio>+Cd z(I7)>^ts;f6)kVFHN0nfa&)}zO->KTYn918&EZLJ&uD9E^5Af5vNo(&*Trj-75Wn& zZLZBVlKFZnRjpQ%*+e4Q7_L@YUM{mX5l^pJwQH*p;!^^t<{_j5hq-&bOrMq3kOm4mU-{S#x} zes6q=;Wfu&lit+y2$syVOGr;94NvW#xVADqHpO)(T%W}Kq*tj2`G>ea zgXB7HX~V`%<;@peyycQhw?1{-_8ph)+;#aCS6=nBtDjz}*6Lnk&)(5%_KocyZ%#aC zvNbh*?SbnKJ~xp}r8C)FzEC`OSZ6C8=f4Zd_Y_-h-FfF<;Fod;&Wm()_w@Gl4-76? zxG1{#geNUIap_6RPPQ3NS-#@b(@u}ATy@6kC!cxN(AnplyJl^CSY0^IW}nz>wdUj` zs28(q1ej{hgZUEP2QbfbtvUL`0PZ*^Qb&MeOSwMDPi?wlmfpzp#K=^$Gw(9;ndLGv z=8bdR`R-)ypEy!cYt}tOCY*aBQKv#%J4k2>&a`OM!%?zaCPr^rEKa-ZRg@E*IE56@ zjpbag@bCGV^RL#N9;?U3n^UpT@zJSK=n_p%?bUJ^nH=Adk%IX=o$JT143Gs~WzY0fOMOs# znQq&Uf9AT2RQFo7$c@`i?-<5 zH^{r1U*Y|kBwe3HGIX=q8Zr3D{W+wdu5-B-kDDB?jEx9&=DlCTeMetS*aZsG0wJtVFZ+H$Xo;nz7 zOjh=L2bz=nVvWk^*z}~gb874&Z`>AI7rP3c+&s{VwGOtXy#2Ap^mxtiz993aouzG? z)|s*}!u?SEQFvr*YIMI^+PLEi`rVrLVq>HGys?8j{p_RTu(D}elRDoX)Ky+JwsG>{ z#8fj@+Ob7ro*u7UOUEkJF>fQBP-2BL>21wqi~}6o+bcETmfshF&F(id)I_N`lb|u!gi8x%-@@tK4Z0_IR9KU2&M}ebsB`DO<9W~tCzj<_QY}di@ zn%|osUpc6+6{}2oX5#9jBHLpJXMwOL*7CgA-l?gH*1F;0db6gbGTcUsGsfxZHLbN? zYpos3S|5Dij&NG|t=|+$csNYb8adU!*E=P23-dm~uW(Y^+;1J$_)aGBI@a?ig}fCAM2K>u$Ru9@nnYyH}>x2ZC4@R9E=*MeFk}(5XZDsLw zG30b_5~P?OhsDBvpdc}gWnFB`eumZ><-s~gR%;3*Vg2o`Kr|CENKe5+I%X6j0e%e8 z*v`Y6*n!chy)0ZFTh+pf1?V2%r$5i9A3?ufz_n!a;9l!b^37xEC*)l?2GMt%TNXr} z3#sRcgMTpNj`IL*RcK3VO)_3^eP$0+SCSTxg8Nr-y}slAHm*D0zk1&NYvepJ4 zycOJEz_sQrxPJ=QF;b2c$lCdN_XY0t?9ib&xoy~ z&EYC)R`Y;YAHld`SdpW?1~Q}8<)jDYK?<;PzT=E7j{ZUVf4lXXH#Roh9IyF0%pvN1 zD(!uZw1K35oqZ9^I`7wW|0ww6@sg>>3e;J%#3cIFo^4s2N4_fh@4`ZmqGFLS?{6yV)OTuW!#LXy2BcrJU4 z`mQQ=>*(82Quc`vUmflWz@XXQY zAhp|Uu`NjW)zPWeM5RWDuiIOJXL|Khmqg8abND@#HeSafP+L2%*me2HhSJ7MFW*_( zSPn35rxA(fOJ?J--AsTlnV}PSmHlmvrq;yJ2J^LS77O0mg7aqDT16Wgo7R>7vD&1_ zNRMbVr^o9P-sFCi4q52x-uS3jr=oF0Y-_S>ph3=k((BSbWmKY$M;(<^ zpaG0u4f;>LI;Jv&J|m^kKyarlZS^In6n$#EI@#QZv2E=`gc-1_V;kxO-RI`b!hPupLG^Z!Q*6MWYpm!b0?o_)9^Q;bR1k>6ilHl69ckL*d z&EOdSq&F_zgUa?-#t)jut)TmAM{j9mTBcy$f;*<+=J=l3c{Aj5a(V(pVF-cJK5HOG zJOu)cnCb%%y4>&WXIh(6dy)GM@j}_g>anLeiKVD+A8l_LOGGCY|w9MLl zxAS{6dIZCSG>tYzmG+WHJpK70D`U;4N#t$IA>FKPjo}L<(f8XZE4l3-X(Zcm-|C`Y z+*j(?R>o^G4vd0FV@zf%R`m`xfid75oeYe3-=QAqzCnG{lo!pKEx)C?OT~lIP`#jR z>|}Ia^3Lx!3v2j5r8PQihv{r3-)ZDKiKMz`$*aZo1ACjS73&-MBl5~#sJua+p4Dx6 zU!ys>zcPgmV}b6xkGzY>H$<{)%jhmusP$9>+ITY&NLJPh?tIF`$UDE* zHeBNI;f^IRtYz04HbqUFb;?VwlZ{$B#gX-l*8;KU9`XuauOg*L!TtSQt9<9Z>>Fwe zA`P#dHSWSvW<@m9jia`R#--7jE;^?zj~Y4t<~7S%`nV+lAjJcEeR!76`{7;D}-*^_VQ`C@(nI{K+RZSXPbAG0>2CiEDh zvPKewRYrHu<+vAU(ssiOC*cp+7pkc7vrWYN-$(sVqyD4HA(a+>y0(SStIGx{J~D#b z(u|w#j_jGPOx9&~8#+5a+8Pzr_91X|J3V?{pxwfHs-TC!{ zRO?`qQ1NzWiXlk_aovq>W){i7*oH>pCZl4_(n$s;vL zzHoKcS@1~K-#($wRBT=UZKp;JO@1A}8uQ!v6}|*ApT&z~&VF(?tmWEKII3Y!*J4vM ztPk<7?)xz-v*^Hte2)6JQGb+Qt*t{eCi-;=DH!h_t~+_f-g)<<++Rc5N0L;ADCmrl z_LDl{G0uJe>~I^RZsvB~y2{vr%E8u%5w3>LUK2AHni1hlz;A9jEo$tRKJ3~x9oQ@R zV`8!i;fGc4v6aT>RB}IMMKk=eHKbTT4)(4^_?F0X^kUB7=}}`RJ?eUyCGOUgjO=VW zwZ`X^w6}@!;#tA`ZEH?#mYlkyEvIfWV(P}Wn7Y$5QflV|gw%@>Qm>e`Wo@&PSI3jt z{3R=)(vttotCtW^w~SxW@^rzAQeaj0>)8Zz*kGj69SEf*D~*ha~x=2pptXdy-}+<;hEZi|J= zp(mSsp>U&wLa(*SNQEuRJ{2)=E0S-^@01FNE|h#Ba4RyPnm_{FBCDg7`>vRk`>tpQ z29%o(MzX!}R3q$dm#}w5rztugR<_wl zUh!Q@8ki)qV-3^VF(hESc?75A?lGWZ#xzb zR1$cPF7{Sh0>HJS&FL1BNEQ7Fm)B?uCIoT4mon#2hP$;!y>2^rEQi@o6vsHi3?M8l(7o_D&k+n%b&yc=HdWiH}(&Cel#Yq{` z2GUN_UefiX7m@BHy@T|AlE$NP2$q74l3=L&03Ld;F$pdjU+}B96FOt4a`T^-Tq+ZtE64IHZIO%-S#iXl9*N~=3 zGo+W0ZYSMATIKfktzIxRG_+!9s5=xsZD`5R;L!3256)OJG_>f1q1e#S%Ko9Qp&{1Y z(9oGfL(5k8EQ}5ftsdgu9pHCp@yX86(iNw>@nI+K?3%8LRXSVno9)3IC07S-M~pr! zGG|j$CMRF-u$CLMY$oN2Z=v-{qQHnId&Jb#L1&hg`6d@svz{>b3+m@wjHW%J*{Okx%5Sb3gF9of2LskjLFp5s=0p&YwE`HTcn>`vR8sm&J z%dH_muy>{aIN{)#1zp=rGB>jk4jhRC)Ams%CY?1SecwH!`uD&45q+0 zdPq&M#oANg!_Us|>y=&SaD#l)*2&LW|}p|HY;1G=kr)0eGMcWXG7%(PN@<#gvNRxG@48S zAPnxAriE^^ZL5PV6&1hgY@v-@fn6zP?RL!?O0$Eo!|KvPfYm5m(k!1HJ{OBgE!Rqb zfz8f6S~vE79&HXnE>yl9fyfKG;hT7b1sM#0+>dsTGp=ow~&Q^N> zHDr(JlUcKVZS7Vyq}gej^SEs?W1JlUB5f3JRQ79Ws)lw}vxNK+wKL9b!xuTVaAb#d zg8L3j@7%@augq~PZFj!&&A^(6Akftq$Y%$%%K%Sp)vRm}#QkUL)$GIBX%Ec6P+th) z99tXC z1y(rk-*jl3^A-1dk-v#7b6?u^Z=t1;e|KKZG5LkAceyvY+rz_=)0_vo-sGGRJ}vxr z?zh6d&OKd@TMzxr^|~^lTS8w9tqpID{I9Ophkg}V(be1ap3tRTCx)(bc81Om|1A7{ z=i}k)BcF5L9zNK$IyCO?53h>6BeKc)wfj!z=J0Ok2X0T~w#c(1=Q>Ywi|#9&zw3If zyD_xc{UuD>*eJQ&9jTXJNR0C>1If|zXWNPB2Qy0!egd44NC#Wdm4n1C>~ zx0kqGZASzd8~-dtjeX%-evi`Vez}3ZL0htU)EP&N-(VcjDlp?=Ju!3II6&E-e)MmT zY^Ftg6YXq_Tia}tF{L`%ISzQ89aP%(aW?i(`Bva~oVwmi-Liem2Kw4y;0?wL`&1tT zf<+VfX4l2OMLEfVt4P1%ny$5f$GF$Obr^1f>m|cBPw?9|EEtQ767Unl{jUwh< zCkKW|Z&lhn(QfE+vK4E+LlJNaaBnm61pgmx@Z(JItG-=u!O9C@mbJZJYu4(v+sH7n z$d+|O#jj-F`}mbSJhz}q_(gdal=+cg_E&zH zpe)t4;jubl;eHe4g0*!s*M~?iAjS0Wd9gM8>0i@-eeC+7A%35gxbCPEN-Pt2GD@A& zZAMAzbApimL@JR^WD?m#E|E_Z62)X9nM|gV>0~CEP3DsMWFc8hB~r;$DwR%UQrT24 zl}{B?#dIQ_OsCT6bS9lm=hFFfAzjQQGRaITlg?x^*-S2z&lEDnY$BV?rn2d5CY#OX zviWQwTg)YL$y_Rz&Si4hTrQW-6>`OVBA?8s^67jgpUvm;`FtT?EF=oaLaLB1WD40r zu8=Pj3dJH_EYf_Ds*4mW@~BZ6gGo$IdyMIJa7$y%G8u{s4({XJ3l|9sKzv1&om6yy za40|``$h_|abF0pY-CaFMM;YvSUq>uWxKiM8i)1X` zDn4lqWsb;8K!6;N!xKbk8tu>WvjpXyOn%{djuhNWhd-C(bh~cYjfA>-dP2RueWCuY zfzZN8)Lk4pq3cPDm$)Z}P70m8U`5xdy{EgY-D@KILhlN{Cv-2;!${;JMt&6fN$jD>uR{M7{x4!Mcul40$_VTy(^q+TOWAxTnEN+f`^d~=kdiCeO`TefTU-R1d z@L5Aw-0_At{>5EydFx+(=-y9s4-B4o>bj?tFT49KpZok9dQLuN)sruL$`611)8Bvc zzDVqhC!aZ#F08xwlC3*-U2)~pp8m{d@2+``eXZ-Rzu`~sdi%THefa)&zH7Yskyk!@ z)$_W-k+tDQ*d30~+;nOC@S=XPz1EIeoC?cJ**t0P0bnSpH^Z_M{C?eAT7 zUbz^q_4Xx}cC8FA?{e1{BA0dzNBVpEde+CziVXJU!t1(D>4^;X?AVe|FG%;qd;4!Z zbLXY!_MWr!lrvW>IjL_O4QyI)a!-Hv#l2_sO%H6i@SN`Ry862>>vp@M;jWn%S5LdR zw}0mDXRj&`^mi|K(z@>c+?vQqGk`+8gw(e^>p|4^eygRF>vzWDGQ9BtcUOc? zUm6Ra8(upQcN5`cDCNE-^tRC3Bk$<_uh6f%ejEB-`1gH(dELPmzvNActDg4aTVA%} zn+q3Ry7kw;i4Q;Jna>*e-l3Pg^yRO3+j~Cr;ZNT8nZN(m_kZwvCt?sfzwW#Xwp{Y8 zLoem!`#$vH`~Ln5U;O?LIDo)M_w$7B&#rriUjEuUKJ$eyE?9i_y7S6gu6o8ZpFL9d zUh?v{QRI`K`S$mJ@X&(AWt7I5Lw|MeM?U)HuRip%n_uwayYBwTM?d+gFMj=>F24P5 zKY!mBzPM%E_N%Ub_Q;>S^w#&h{{tWW*nOY+>f)s*J>!|b{LkP0erEr3zV+>er;j&R zoH}y-^WXWd8$NvR(vwa*ee*@zwhRBCf5TsY@_{dZV)J!|+UKOJx8FL=s^^2=VnYtQtjKXd<= z9{k!5fA@PQHnQrbZ%1z0)Vn;=z4*p^7S6n*>-4@GmxoX8btA)(Or$65_H_3w?%%QK zgq|yU!jTpIec|44PdEhiAB=Q`2fE#bCw6V?S>AJ1PpJE(!5xu};kA&b#odbr*F{c! z@kJ-ixDJ~cdZSMT}ZMd9;%3ccrc-Sqp#%X)_wuMMwUv~tnREs>jEee%GGFS@O3 zxa&MHZCT&UM^;S@&V1#R!LFI#cg=iz@ZawU=lgDa=8~BY_Rf5^tAE*f;r{MI@8;gY z?x}&(!cUJ}-8XadvK9SH`?f}A{Q4MIGBUDVM2 z4Ee;fo;m$ICwNxn{tS{fmfjqRIWO&q>7AH1Gb- zn$TN!uRZP8yW`I9V!1nizdQF^_q#c_e`S94g74Hy3sq zKXe-B+_-!ft{YGm5{-s*zamtme?cp!x)-|KW{< zx}m;-o-Lu})aSbSg)S|24Y;fO+(yLhrcSavVhBOcaaP1!>5K$aab@IL_rdfTHKL%rSoAvbY)GLqoF%N^<)4ab6?v6R#qai2a_QygyLeNI~esZYGeRb%R6Bf8<_MSWt z4<{H~D0G&)5y*%5=3uWo>~cIwI27t)+~Rf%`G~carTFk)Y;=+5MJHo_Fl+`6f=Dge0L-)Py!V94maF;qJgaI zE_E07gu6c8tHGQks4`c|=Z5~3zIF4vJak2`o?Ih%@YV}6sa;N=8~Oz^4@S5zqrHe5 z>&Fx?e83%-2# zpu|pvq%1fSLG&#jb|bQ#x_5rq{g;rledOueGKx=1rdi1&L5L^&s`YTBHC~x$?QKqt z7^j+dAel^{im-!;oKwmC-fS(M%@?cHO0kj4rBm6mdodNJQg*M-h>Qv&?HIpSDorW- zP{x+o7u4is8_7nZm`$_iIhC$v%Aw02ZP&7m`iShjs5Mi5|8m7lz1k?C%2g}*Tsm0} zU-~F(%~L5R^g3OvHma$7EsHkTNb5m4vh`6`R-iHy=0$DkOuY`&Qk8l(RcJI4<*uhb z%G!z(m8rcWvUqDIiiu>dUT5EOHIuJqD&_9ABO_RgOfc%m(I<1n4+RmhdeX zD%5+`Vz;LS1u%f{`#|zssyfx5n=YgxsO~7>uGI-VA`p{-ul>nV*^}p zs|)-KnUU37FJCPr_xGj7<7j96ACGU`TjBp?Y9j&vtEW?m)s>d{v6*=okDEx`c-(|T z>>S@?a$a=#>O!!tn5cZCny#cPUL}zSBXhZOd~EF6{qX|!2CN+U6ow7<_a`!$+`>cZ zQ88h9WVd9+?91Hm4%2reDdd2}IQvR=bmn{xxdJ zT&>{I$yB#KcYUluXx`!l3RD82m>;&j zozO$1n4B0-%4+$@RG3C}O%{!z!*1Ojk)>>8QUnX)QmN+?$!xV+PkHHjc}WkeTR~FSM-v}S`xCIE|yQuwethGtcizAX6jA0CKDN{ zRWRA2om{%;6{~5CWVDj56w0R*CSIYK4964QtR;(?d_GYu z0)~7pStu_rt3?ZHnmiUbz3TLy4q#SmiApY8PZjeu%y9K&dBu5t2OxwBk;4X)O6V93 zI1Be9+KV@`x7plBABw{097efxCY3KFD&cWi!=6qnNDa z6XnyylVBw}XC!A=Vlq2#B_`8$DfxR5=ikCcGPPVLQ_R$|xmq&m70ajZ5;%lN838*_ zaQWV5y_HItg)fW0fso#rMVqrCHh2JlpkfBo3YA9MgZ@{T)IvU0j%jx6a+re!$rNZn zye5(3Y~&PVu(Vcdl}0iLGcRUq<(0=fukc$hl{T}1Wi^*AuM$k#OR#_k3qRsitJvT? znGwQ}3pD2yU{H-hfm2LswMHUYKI6=GAv&53*kd7)Z6p%ptuouGRHnST*DO<3z>_bd zgb;6b-setTsxY#8I$vFF{)qVydBk;H59+JwL<*mHBU|CvfOM*Wg}Hp@`Ba%T4dlQ` zFe$go{3NsO&W})0>ma4`jYc->r7#XB^BgadDWA26(m5K1b9B&y>NFl?wkXHroRw0l zS8%c&3)ju^(c~8G3Zf4*L>D1axoiU}LQ0f}0;FZhC5r*JN<85d4Y;{hTPBZWkxJFl z$$BMGsTIm+2ZhqO!;P$$(KfM+BBwHv&?r<*c-d64ku2s5IZm4K%I8coq(Z)9NOUP4 zHz0_|wV`UG*?4^9+9e2&jK*8V$aPnN5X9qK{40$zL%^G%#+76yU&$tG^-7+@Zc^oQ zGyc@38m(j|7#K}6Bk(FHAf3&n7+?Xzy)m!DLD#IOR7Pw#qf;z$Hik2qx%}dt<85(9(%?Uria3fG(Nwq@%d%6jVoyvhcWS5 zf;B#^N-G)M9I1SzkjmAo)kdzIJbM;01~6@KZ4I^jwVRI!*{uV%dhZlP+v(two}%h?H<&Ra6MdgtcVwAMafh- zcj}0drO}mYAP7R@q&H^7aMM^`tkjYbNdP$;@RCf~Uy#NkFsQ=P#M|Um8;u6CdO>0V zZla<%njxSaq^)TEe@2Lb`v;ZO@l$z?BJQtru9iklTep_N$%HhZkrn{-iWZNNi-k~$ zTDFkEW#(m(6F{i*wA4&ndEewyY@6@1xE*U^oB4b(UCU4!WPYh+s?%Zw zJLG7Ey_|z3#>Bkk*bcUDdV;#@xk9><<*+WiZC;_8E?>|)!mLan4qYhn+h$gUfN&<0 zo5bQkBTfU!60kBR0@ox&~op0o_*@l-YKP8X@EaagMJ{vc4;){U39FWV`GF2~| zFVxZ%P`goHe-=%PnWRyM@+|$yk*35QF9%Vod4*!4miH=NvRoQaZzpSeCt0Bz&hbmJ zvMkGLFULYLovJ4&QY%#CiY#xOkr*EcO2VvG-f9_b)=u+~%4R%n`G9l_P{!i2WYc(D zfeG;|y_#0XbYbzAbD;>7)W3 z4Eaa_J&{OzIgqN5PNI`l6XlDZ=z(0E&{UX7m@8>yCZ=0^fh}I{M!krkp;oVDYSm(S zi+Ipnp)+SyI7CoiTS_GAnS2V_nkAm7q6A!`X4)$%jgLJIw&j`Ck?CB9W|GwcRsxnn zs+i4JlJ)YX&zxT(YbEqHmUcqY$mX4RI-O1l8-2y6;&FUkC13S$w%4=uYI*Cq)UGv< z5w*4gFIUu6}yUhEvJ6RktA{qETLhzdimC-Wl z907cSX>l;uXr5ZKmo>(ykow)eg(W5i>eFgVhF_G88P6 zDth@;HCNs_9Dp+rBcXkHBUDr*Q=t)6iioR~Jj5Bzsk|$thONwlU~fp!zM8Cg<;#mfnXJ*ib5fW0P9`(<7tIo3%1n1UTd3u$d8~W&Y8nl@e8n7vr_DnO zmW6mzStfr{;(M}2Q@I*iNWNGkcB6ddN%NU{rAo-~)ZVMw>{w{st}>Z0bzVNrSypYX zgow^UDgcG5m#^e%<)=NHp&V1iFQDE}jn>Az<5lyEuugO|FgBS+??{xdJ~mls+j4Rk zRJ}~YYt#veNmMK4r(Z{>iWV6R$}*Se`8v!B<5jTHFdoO=iwxT~X>17I91D5u*0Bx@ zhoCkJ7zcE*-nutnb0ydF5`v*})UaHYZWia6- z%gtzpQ|A>$|7dAv5xOCQ(kSP6Zu+#lU>mr8)Z+X z%b-T1p{I~RQwH9WIjK;^DurCWjy;54q*IN0x$!3wEQyjcwB0OcBMq6Is&NypBOfre7 z2}9P{@ed?9JCGtlg?NgJSgwo2FxJcak1~+r&dPx+t&-42_GC_kWAa*H!%jS3$V_A^ zMZ9czVk;ZE1~x>b@hqaPmHEQc3LH0h~lP?TP^z@PKQl}s&iEO%X-6OC3OuCV&BYb5GNW_>q%LgC1Oah}O z4weF|se+cBEk9RfX0<<~?6rQRI+EKPpDgTkoS>j7s!xEB3I5q}A|2PUb)4?&h>gqYn)YW3O*1&hz zK!mxzn-=KX^ChCr;cG`)p0RFbGqqx)im4IF%`4wvq^F~VBw&g^!oLrYGL33JS*<71 zu<2ZR#??wOT1Q4FqL~JhdD*%*aw}QM$dz*2OpQPg(FriAVHHjjEz1rrn2v7~uVZCS zL-e61n47EF@{O)MYZ_16H=h_0H4M5O|bWU{qv6^SxazUi0+kOwHxNZ>w5n0RwHlz(qD4^YB-VG^^KO zWNrPiyVN)2cF3#JbzbN$KH|lT+^6{i?leapMX-28dopmWBs|O-5S?tjo-HG~1oI@_ zN(PFoweB3v+_051^(HU&t~0VqpmLASu{a=$6Z??GYyxM?=IZ4?b)|r{^-t5nu{HWD zCfJ2~L<b%zKGcy-1`_hWLd{`K9g_K%6Z2(LR%{ z5steZ(dJ;u95_-_Nwx#}s3#AVkBp)j&?_vIh-JA%M$X(@-5!X>cy0g0%iL#CYtEWT zgV+WCpxO8)bKvZwQ9&TR`Ft8nE{ZS~kwW?9ZZ~Tmo6sv&YN`1l31(}OM=B=9LU5!Jm+Ilwi7F1k2JF63ezl=n zS?iM$8;r;2JAsZ>xv^``F8X}7UP05xs)}6M$W_X>yW+=&s`xmbOxx2Leg6!jF2ag2 zDyLX&+B4*p|9lQvH)>thpl}*rlL}x>z?F>i6&n&3oAPViKUBxWWiAOSm8}*s1O<5I z*P4#c7bLVii3gBXfqpj^qgP4krzHbIqN=U$}ITCyn*8_tK>xF;X7CiG<<+>#Bi zj(sgbbO7%4RHJ-{dqDuVxs;~U#WPE4@bA^qnF8Jmj5(D;zI>;_KLdAb8N}bh2B}oe zhWP|eSsqC!GIPOz>a`TIR6bp;CeY||>GJE{bwM{eSX#m0AgZl)>Jji_eP0Fi)VlS5 zg6{X#*@o-x?GRb&S6*3yvb^K1PdUu$j@0x)sp4EaJwY~HEVad zlH{PtZ+4Ao#kV4x6=h5&n1=SSx_0VEWN%CTIz#e~4@>g78gfq>BS=1*Ef5QGx7$A( zzw;J%!N|yDoxm*{p!HVwLcnk3cSC}bxzeO@h_!XyqHH!Jtudp#X1T>%NlGrTg?FUS`Od7SAItaRcV?->XT{vt)N+|f>SY9@eRDW&8H_2#Z22#b zB;=Om%hw^+ul6-BiE!R3oJY>S2RyWM`4f}!0blka(~bkR#- zd_|*3Wf9>MUatICkF};oii2vsTnbk}4S?0k?|qcZqn5^lTWb_Cp4JIl$(P^fcGzZR z^TjbB)@jE42xi|SY_oVP2)NESh%(GpSw-c)cCTgD0_90U{^RNeZ?COHh5)8qBh`SU zcsYX7-tR_Q)yaJya7StX4`0N|Bi-#d8c z0t8ItU6O$Jzi~}O#!<*qXV`d#fDGak`KT39pA{n7KjfO2()|-3c8kHfFsg5gNI!)^ z`upEuZy4@5n;h&W)-TmaR~v{NX&hGB^1X(>`w~;f+;gy3e0z&X=1$7i@xCL*Ihm0v zP4vmwh>2#RnQ9)sn`zXUjC8SH$d>=sh~BjW^^dr+?6lW#CUwLbKIQ_YUgj9{L_M3V z64XLqT=}DhvL}P#o0Odin3%BfOn{2-C;y3VB?@80YzU zi&Fb@*-DYF@D;m!`4f+o>=iJ@)}_G~0b`?HD}T~xi+@OL%N!RXQb@skV-NEy`~C~% z`<~$XKlKFH|7rIHW*31~hRmumWZ_|)M7wkZcWu|yWZ?WcLYnh+wjH7`K_+--r-!JT z#I>HpmRo1miLEJr<_R9?-@9u9k}@mW`-V%x-x}!#21KYdF~2Ou@@HMS!siOnFnAOp ztJ=4}8PG}j9!BHI@sUP8M407Ml;~c?^uzcGK4&1|vm#q1Ew>r%qivZ`Hbs(nNvxn@Ki^k|9nrVdaqGE{&jaImztgLA7 z`>gB-LL?KjmISdKOyPv7VgQlzuU0GH@4n)R-r|xfw*8CNhGeYfBRa8)<5gt7mPD3U zIAEoL(pycHzvLdnem*aDK4s$;@K*B8Au1~72(_r?*a_z`C)H~CA08{vQv}Bo%T=i7 zyh@g>9p!&~tTh!H1v{9<{HahujSnZt&pW8B&7G=d+(j} zN(w0?A@tr!Cq03XZbZ(>*5pu5}(sFLMEA;jsnA!~U0)|IuRkR;Vr0Pnc!q-peRD{L2VqdK*| zRscxdt?Ju{N?ONo4l(~}(%l?|DIr~w6|Z?#RiJbvS4p-wk-p<--{8*Dvr{`S&tUTU z!vGcA%LbC@ihz*>PDmi$3iMrTy!-$hcBkrm@Ph1n$K6QDJY zg@hPj+pUnb2++le01NB;N*QJvE`^nIDfqI(W&oI&K#}A^t6}N;&28fNfI-gD9=2UT zd(aY&AcaHQb)1lzNu{bE$TNIxiAK-NWszdevy4PR@LnK>c|f@;#EK6Zo6+mG%y1!F zVVJ0#a$o8+z-${rNppi%6zN};avELx6I*dpQMHs;1y0%WY806%On5l3zb=h)S!?yZD}sM3CR<>O)_R(Nuxn7VlNz# z?Dy>P)u4qPZb6c&HrVg=?@SIcTs-*uIKGt_t&OH*#dIAx<(BqfP65T*YOYrei=)Oi)j4X(C3}0Y z@yJvwRIzQCCtk(Iy$F`^Q2#P8Jny*Q?wIKl-w1g$8|YWHS+DwV`dj+1NA_}mTdm^( z8Y7@QPcga{d0Vh=0_Nrj2>FmxAk3_bXGuqvteFr2 zDUI$EKHP3$HZ4}VKBkh^J3-p7&X|sH-s94q0v83wlyRjELNi&9AaQLEwpFW4aN^IKGctz{kl;ko(QZ&CV^zrQjN(Knjg@T zO$$L6ko0~f?dy{Ww?1F zkfex7f?Q&3YJd#&^Obl3jW#}IyWPpEUHv9q*H+bpolQQb3TnLuv{wQ@qZLv~WDHG`BEHv`+kSrF$7HH@fL)h|*4 zkF7S$%7^UT!l@Qxk(LEO zbS*l|Z8}KHI_y#uk>NRDo*h5{&IS0FKTl=z_yz~lnW zf|;WB(b8{Ff|E=bXT6=dF6$a!x$8x1Oa=UDSsy{eYdQLjD#leEi-tUV39*F}3IQ29A}mAwGYUa(R*#C?_t@M2`izi8EdzA`+aWL6X!*8&i+UvP zfEdv6*l_@QPJYyJ5Gf=1T!HIlW4_csB(Pm}j3 zgXlT9^j?)Ap{hW6+*WdsMC0iPB#jSIj6$yD_GNR0SZBkNWe=>u&8X}587C#nlceZFH)~KP1iyD2uPfV zPozJf&J_b&bnnd+iX*5#7O=y%f|wGqDn!VEnL?P=A52;UpS4m&BsY z^B3=(<-=Sjr{;Q)F`6U{AmTVyO@BnpstG*ItW32Ph&lF0hd!zVBifhpYS;tj=7^A> z(}o_52rYnS61oK`4;b4f+q3k?S9>LtMo=De#aXJrF%I=7 zR=?7r>=LKC=zlq(YmsF9M&QU`nE@f^ zCy4Y5vpURxM>k+Bp?2bB3D)^aC8_c_g%SU2yD_aS&Q!#UN6af294mq~j$h7Z;OZ}_ zqpd>m<$(j?N>Qv@4_y3FqDD)9rMQ@w1^v3E%ReL&98~}oi$<3#c*?J;OrfqAJ=M*b zoAd`qJv2E0b?j^h z;4LlUk^MZ(NL~adfH?Ib(D|A8+M9_d|vr!;IVGV|;#OtuXzg2Y6xqYnT(w2x= z*)~F<1jx3}Srs6J&?V8|R!0{x*V-Qojs#Ug9X3NqWE3ROulkM>B~q~iy~(_lMzRNx z(?n?qh0&Z8MxQzW1;FUwH25y)LR){=Oy{*j@9OCj<9szq8TxgPYBXDuzDkYFa2n~t z_tbHT#mNLdzppmV%2HY7wp#GuSic?jX znOCcc!Ur-le`NANZB`W@6fj&P zbPt>oU^a@uKT!!%I&l_%s>WkY(`(eINM`;_Z8P068*A@|NFdDrTwN|9fIOG5*^W3M z)2z1`AOW&Cm4dWvLUrKSA(eU;;-3D6+Rsz0eQxx=orA=dA61%2w0TuX*!o&=qmBP! zN^UVd{+DXZbnSI&EL@zOUAi7uJjbov(B66SD)Tq0oXvna!n$=;4IoRzt|tQqmprUk zmXJ%yzafTklgfEl(!RSyl-1d3PGT_x!a;!)-O>n5+Rd>kHl8uftKGMc_q2j58y5qF z@(o)wBvInXK{xsqwS(?2DrD3o29ZhDf}d5PM%ls@rO2vpUE4F?rZV1xpw+8Z?=F)S zBdI-}pbFU`XaPQ|he$z!=(t^-!^6g&Eth}<-`VRdy*U4!fvI9gKE9yEnk6ga!s@77 z&`$Lo`7T)8h-ATC!qF={Lj_EO6i=)Ms}g&!?<|B(GIHI|kQ88P>;ilR%+7>|!GhYP zzDvDb)?D@m99-1(3X)_>e3ACF?-4CGB^^Q~hs>MP^r&GHA?v$Uu6QGF*If-WCDOPx zULRJlVo|6Ca!n%e9`%&s+f}-&loku`+UKD5MRpee z6YNzW2`xSH&C*6@Vz?L(D5(tsm{|1pM5WPlDg2CVPIiSToN6wuqw>t3FR&Za)^zQZ z9TzVw9Gu^^bLSoua2K1?Xq4;}k!kbK6b*$6e2NcO+(t_e)DxnV5fBO=?S>Gx8|h|F z-a=QyuZ5O#+L##3;8c6ssZU!|2juWE&Q{zJk#TR@q?J#BD^s1a?J3(jZTwQ;=8N;= z)^x?1wnwfl?P%97p6AYc=MK!wbdilOoAjw?Nk)Hi*JOIE<>VQY^C&*~3lu0OcU?9q z`riDxt1;h8&k>!U+;w68#W{p-4=l{{_k|_7fPgD^n_@MWIcv!r9XomW@I_}#LSf`w zaq|H2C3=7LqQi$rZY!a!UQ}f6|5nD)T+^$WD;(SRyQcKyKJRDX^f_~Um>274+V71- zzS4~HF&Z)RSY4u~L%&$p6^`}$?d&sR$x`WhzG2?5-!=NZqZ}Q>-;UslyAnU8_lnyc zA?(Llo#)Bcj{?v_+l!qGGVM^vvm}P2>)ZUvNCvUwFPl6E>k<~thn%=Pb8z3FcR3@o zHzKq{ri7a0%G;!wS5ul;c{7&DV6%p?dU)NbeY);x_!+tU+_L@F{?!4UmMGh0uaXSH zt_vr*TZiZ!UrMbomUV3%nRdRJuA+4$qc_Y*T+E@_Lp8W`PS8C=HCagKX+C%tnE1%d zyTXroul1U=d8ZXSu=w*~*{QBtcUqsp?;eU}CqVbtm!AZ~bi2e_!w;4me@+lLdoY`4 z=QnC?n|eN*78dsB->O#nd@DwZ_GPTJ`b>r4djSnGH79xJ?7bcJJ4jkvhVfw zpNeaIA;js*zCUc|k-6Z8LpGPrypkwm1)UkYiNkhju@%quztFKO8Hi#}_W8baoi_ne zSrS|=#NhGC=hvOSskyj+-xT_+bBC5rpO{@VrKG7n`{$aRe0%x^StL_PfsZ^oK4F+R zAaJ88ar5WLBua3mt&tN);OG+gZ!XDc!knYb|M;)8i5p19*Zhr-%E|A9ru=nVmt7oP zpKRFWa=I7n4a-~FHrVp0Y@FrN@!b|}z2e)Oi`J;TF|#u3*I`7)$735&-k7cZGsny# zE|<2p?~&sh4knx3*7e1wd3^e09lKO{eX{$MH)gv^xt#A7jmFmT()J#!cXUI#Le-dA zfMqJYvarsS|MSV2rIXv56=!JQ#^xe4E{~Kp07W%HRJkcLEw_EXVqvAfAHtA0s zV#o1;PkJh+qj6}U94uGjgu2p};)m;#4=0KrZb&}dR{T&7@ssA;iyxMg57%wVju{!5 z=+Mq8b*6SZlfUaas}iFfb&IiXjjhdn^tiTicc{yGtaaSudWR~r8o(Ld-1Uk}j91nm@6GyYm6NwY0*QTxi0>Nykpa1{> literal 143043 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 -> (String, String) { } fn vault_dir() -> PathBuf { - let particle_id = get_call_parameters().particle_id; + let particle_id = get_call_parameters().particle.id; let vault = Path::new("/tmp").join("vault").join(particle_id); vault diff --git a/crates/nox-tests/tests/tetraplets/Cargo.lock b/crates/nox-tests/tests/tetraplets/Cargo.lock index 2b7e1963df..00c3dd1b43 100644 --- a/crates/nox-tests/tests/tetraplets/Cargo.lock +++ b/crates/nox-tests/tests/tetraplets/Cargo.lock @@ -2,12 +2,39 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + +[[package]] +name = "cc" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" + [[package]] name = "cfg-if" version = "1.0.0" @@ -16,54 +43,90 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ - "libc", - "num-integer", + "android-tzdata", + "iana-time-zone", + "js-sys", "num-traits", - "time", - "winapi", + "wasm-bindgen", + "windows-targets", ] [[package]] -name = "getrandom" -version = "0.2.7" +name = "core-foundation-sys" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "libc" -version = "0.2.126" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "marine-call-parameters" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05495180730abae04abe209386ce367309a82110edb65fcdb1f3080f819bc1a0" dependencies = [ - "cfg-if", + "marine-macro", + "marine-rs-sdk-main", + "serde", ] [[package]] name = "marine-macro" -version = "0.7.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f21b40612e9da310a6df1e394cc30b4962bb4ddc13ee50faec6d2704861b7b" +checksum = "f502185316f584a9373cceb6ff24a11d260dfd39505c817056bc127cd1a96a08" dependencies = [ "marine-macro-impl", "marine-rs-sdk-main", @@ -71,36 +134,35 @@ dependencies = [ [[package]] name = "marine-macro-impl" -version = "0.7.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43b2b1e5bc5aebdaac7029cfd84a038cd11b23a023e724548f863618716f44e" +checksum = "e50fbc0e70ee4cde7802f0748acfb197d7770c7feffb980ce8c29bddd007519e" dependencies = [ "proc-macro2", "quote", "serde", "serde_json", - "syn", - "uuid", + "syn 1.0.109", ] [[package]] name = "marine-rs-sdk" -version = "0.7.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4208762a010c41a6352a651061fcd1a7bb077c6a3548be8ccd84fe79c3e1ddbd" +checksum = "f93d2bd852fea1fea8097c195044430347eda98fd6a3752119b549192d5ac4ba" dependencies = [ + "marine-call-parameters", "marine-macro", "marine-rs-sdk-main", "marine-timestamp-macro", - "polyplets", "serde", ] [[package]] name = "marine-rs-sdk-main" -version = "0.7.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0751f4093511b4d8942bed9402dba70ff927cb4df9b18632443294d1cbc51e1a" +checksum = "0b79c165fc21438b069babeec5ae36ba0eade5e08fb1d92dabbe6b41014ce841" dependencies = [ "log", "serde", @@ -108,99 +170,78 @@ dependencies = [ [[package]] name = "marine-timestamp-macro" -version = "0.7.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5297d57cecd75d71feb0c94da9027891e3c31215e23425b0e5dea9a5a7e60230" +checksum = "d03f267ac0a29f543ef12a1a519ff8d98e74ac66e1c580f2930d41ce2c50507d" dependencies = [ "chrono", "quote", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "polyplets" -version = "0.3.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "407811d09f3e0fb2086060afdfbad2e6bb5b63c300edeec93358bb5b8a8e4257" -dependencies = [ - "marine-macro", - "marine-rs-sdk-main", - "serde", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "proc-macro2" -version = "1.0.42" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" -version = "1.0.141" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af873f2c95b99fcb0bd0fe622a43e29514658873c8ceba88c4cb88833a22500" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.141" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -209,9 +250,20 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -227,61 +279,127 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.1.44" +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] -name = "unicode-ident" -version = "1.0.2" +name = "wasm-bindgen-backend" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.50", + "wasm-bindgen-shared", +] [[package]] -name = "uuid" -version = "0.8.2" +name = "wasm-bindgen-macro" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ - "getrandom", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "wasm-bindgen-macro-support" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasm-bindgen-shared" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-targets", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows_x86_64_msvc" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" diff --git a/crates/nox-tests/tests/tetraplets/Cargo.toml b/crates/nox-tests/tests/tetraplets/Cargo.toml index 57548652d4..bc2df72b8b 100644 --- a/crates/nox-tests/tests/tetraplets/Cargo.toml +++ b/crates/nox-tests/tests/tetraplets/Cargo.toml @@ -1,3 +1,5 @@ +[workspace] + [package] name = "tetraplets" version = "0.2.0" diff --git a/crates/nox-tests/tests/tetraplets/artifacts/tetraplets.wasm b/crates/nox-tests/tests/tetraplets/artifacts/tetraplets.wasm index cb9a26d5585c3280de356232ffcad396d0d85cbc..10e43e26ecf60ebbf3790744318165afeb9dea0b 100755 GIT binary patch delta 41348 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 delta 23765 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 O$<{S9SVYxeqx>HV5i3am From e472b53b669a9b10aceaa99f3130673236329860 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:29:30 +0300 Subject: [PATCH 46/53] fix --- crates/nox-tests/tests/services.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/nox-tests/tests/services.rs b/crates/nox-tests/tests/services.rs index 817180e162..688c814328 100644 --- a/crates/nox-tests/tests/services.rs +++ b/crates/nox-tests/tests/services.rs @@ -150,6 +150,11 @@ async fn create_service_from_config() { "modules": [ { "name": "pure_base64", + "preopened_files": [ + [ + "tmp" + ] + ], "mem_pages_count": [ 123 ], @@ -163,7 +168,9 @@ async fn create_service_from_config() { "logging_mask": [ 4 ], - "envs": {}, + "envs": [ + [] + ], "mounted_binaries": [ [ [ @@ -328,7 +335,9 @@ async fn handle_same_dir_in_preopens_and_mapped_dirs() { "logging_mask": [ 4 ], - "envs": {}, + "envs": [ + [] + ], "mounted_binaries": [ [ [ From 7f1f757b7c8957393079a5ae36405c2462ce02a4 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:30:19 +0300 Subject: [PATCH 47/53] fix --- crates/nox-tests/tests/modules.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/nox-tests/tests/modules.rs b/crates/nox-tests/tests/modules.rs index 8a0fe492c2..a8ebf3423a 100644 --- a/crates/nox-tests/tests/modules.rs +++ b/crates/nox-tests/tests/modules.rs @@ -41,13 +41,11 @@ async fn test_add_module_mounted_binaries() { "logger_enabled": true, "wasi": { "envs": json!({}), + "preopened_files": vec!["/tmp"], "mapped_dirs": json!({}), }, "mounted_binaries": json!({"cmd": "/usr/bin/curl"}) }); - - let a = config.to_string(); - println!("{}", a); let script = r#" (xor From 3f418a99289412d3b8b46725899c50cc7fb1cb0b Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:40:33 +0300 Subject: [PATCH 48/53] fix --- particle-builtins/src/builtins.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/particle-builtins/src/builtins.rs b/particle-builtins/src/builtins.rs index d101e8893a..163a71c875 100644 --- a/particle-builtins/src/builtins.rs +++ b/particle-builtins/src/builtins.rs @@ -1018,6 +1018,7 @@ fn make_module_config(args: Args) -> Result { // These are not used anymore, keep them for backward compatibility, because args are positional let _mem_pages_count: Option = Args::next_opt("mem_pages_count", &mut args)?; let _max_heap_size: Option = Args::next_opt("max_heap_size", &mut args)?; + let _preopened_files: Option> = Args::next_opt("preopened_files", &mut args)?; let logger_enabled = Args::next_opt("logger_enabled", &mut args)?; let envs = Args::next_opt("envs", &mut args)?.map(table); From 09aa8aa77f8fb59e46ced287e87236fb3c396fcf Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 16:50:57 +0300 Subject: [PATCH 49/53] fix --- particle-builtins/src/builtins.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/particle-builtins/src/builtins.rs b/particle-builtins/src/builtins.rs index 163a71c875..00ce932b15 100644 --- a/particle-builtins/src/builtins.rs +++ b/particle-builtins/src/builtins.rs @@ -1018,9 +1018,10 @@ fn make_module_config(args: Args) -> Result { // These are not used anymore, keep them for backward compatibility, because args are positional let _mem_pages_count: Option = Args::next_opt("mem_pages_count", &mut args)?; let _max_heap_size: Option = Args::next_opt("max_heap_size", &mut args)?; - let _preopened_files: Option> = Args::next_opt("preopened_files", &mut args)?; let logger_enabled = Args::next_opt("logger_enabled", &mut args)?; + // These are not used anymore, keep them for backward compatibility, because args are positional + let _preopened_files: Option> = Args::next_opt("preopened_files", &mut args)?; let envs = Args::next_opt("envs", &mut args)?.map(table); let mapped_dirs = Args::next_opt("mapped_dirs", &mut args)?.map(table); let mounted_binaries = Args::next_opt("mounted_binaries", &mut args)?.map(table); From 3a1544e127638b4fdb5a38a792a92eb531cb4508 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 18:46:23 +0300 Subject: [PATCH 50/53] update fluence-spell-dtos --- Cargo.lock | 39 ++++++++------------------------------- Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc02c271ea..780184ecc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2260,9 +2260,9 @@ dependencies = [ [[package]] name = "fluence-spell-distro" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2a6f192674b298a5f49265be8b5d69479170bf4146297b28e3324b2b91f1a8" +checksum = "b4f7a6eb40ae202258affc0cde4ec51e4b49dfb01e44b2b01fe97352beaa46a1" dependencies = [ "built 0.7.1", "maplit", @@ -2270,12 +2270,12 @@ dependencies = [ [[package]] name = "fluence-spell-dtos" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23727f355efdfa8f2d2c54822666ba83647d04aa614e20ae158074ee6edab0" +checksum = "13c65a23ae0bd9c838c3dba0ed167222e36026d7ce0f29f89faf5a47eff28148" dependencies = [ "eyre", - "marine-rs-sdk 0.12.0", + "marine-rs-sdk 0.14.0", "marine-sqlite-connector", "serde", "thiserror", @@ -4638,19 +4638,6 @@ dependencies = [ "serde", ] -[[package]] -name = "marine-rs-sdk" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7662e4c6c08f7cd1b63cd6ff8e473e3239a9d3dd10fb46bec357cb8331423268" -dependencies = [ - "marine-call-parameters 0.12.0", - "marine-macro 0.12.0", - "marine-rs-sdk-main 0.12.0", - "marine-timestamp-macro 0.12.0", - "serde", -] - [[package]] name = "marine-rs-sdk" version = "0.14.0" @@ -4767,12 +4754,12 @@ dependencies = [ [[package]] name = "marine-sqlite-connector" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb21d5419382307d97677d92c267f714a77549658270ada5d84496bfd74314e9" +checksum = "1c4d82c7e8c07f59aa526271b810c97ec7f79d14a77725025618a79b0b1d9051" dependencies = [ "bytesize", - "marine-rs-sdk 0.12.0", + "marine-rs-sdk 0.14.0", ] [[package]] @@ -4785,16 +4772,6 @@ dependencies = [ "quote", ] -[[package]] -name = "marine-timestamp-macro" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ec0d664570661ecba1feba2dda610ab63f23733edf189dcde7b5ed23966144" -dependencies = [ - "chrono", - "quote", -] - [[package]] name = "marine-timestamp-macro" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index 52a44dbaec..8dc067b34c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,8 +101,8 @@ types = { path = "crates/types" } core-manager = { path = "crates/core-manager" } # spell -fluence-spell-dtos = "=0.7.3" -fluence-spell-distro = "=0.7.3" +fluence-spell-dtos = "=0.7.4" +fluence-spell-distro = "=0.7.4" # marine fluence-app-service = "0.35.0" From 6bde1de256531ced8135120b0841cc4d06e808ad Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 19:07:47 +0300 Subject: [PATCH 51/53] make injecttion async --- crates/nox-tests/tests/modules.rs | 2 +- particle-builtins/src/builtins.rs | 5 +-- particle-services/src/app_services.rs | 62 ++++++++++++++++----------- particle-services/src/error.rs | 6 +++ 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/crates/nox-tests/tests/modules.rs b/crates/nox-tests/tests/modules.rs index a8ebf3423a..5b138d9e5a 100644 --- a/crates/nox-tests/tests/modules.rs +++ b/crates/nox-tests/tests/modules.rs @@ -46,7 +46,7 @@ async fn test_add_module_mounted_binaries() { }, "mounted_binaries": json!({"cmd": "/usr/bin/curl"}) }); - + let script = r#" (xor (seq diff --git a/particle-builtins/src/builtins.rs b/particle-builtins/src/builtins.rs index 00ce932b15..58bf2484c7 100644 --- a/particle-builtins/src/builtins.rs +++ b/particle-builtins/src/builtins.rs @@ -1033,10 +1033,7 @@ fn make_module_config(args: Args) -> Result { file_name: None, config: ModuleConfig { logger_enabled, - wasi: Some(WASIConfig { - envs, - mapped_dirs, - }), + wasi: Some(WASIConfig { envs, mapped_dirs }), mounted_binaries, logging_mask, }, diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index fa8269821d..69be2c3a8f 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -19,7 +19,6 @@ use std::time::{Duration, Instant}; use std::{collections::HashMap, sync::Arc}; use derivative::Derivative; -use eyre::eyre; use fluence_app_service::{ AppService, AppServiceConfig, AppServiceError, CallParameters, MarineConfig, MarineError, MarineWASIConfig, ModuleDescriptor, SecurityTetraplet, ServiceInterface, @@ -51,7 +50,8 @@ use crate::error::ServiceError::{AliasAsServiceId, Forbidden, NoSuchAlias}; use crate::health::PersistedServiceHealth; use crate::persistence::{load_persisted_services, remove_persisted_service, PersistedService}; use crate::ServiceError::{ - ForbiddenAlias, ForbiddenAliasRoot, ForbiddenAliasWorker, InternalError, NoSuchService, + FailedToCreateDirectory, ForbiddenAlias, ForbiddenAliasRoot, ForbiddenAliasWorker, + InternalError, NoSuchService, }; type ServiceId = String; @@ -1001,17 +1001,21 @@ impl ParticleAppServices { } } - fn inject_persistent_dirs( + async fn inject_persistent_dirs( &self, module: &mut ModuleDescriptor, persistent_dir: &Path, - ) -> eyre::Result<()> { + ) -> Result<(), ServiceError> { let module_dir = persistent_dir.join(&module.import_name); - //todo: add error & make async - std::fs::create_dir_all(&module_dir)?; + tokio::fs::create_dir_all(&module_dir) + .await + .map_err(|err| FailedToCreateDirectory { + path: module_dir.clone(), + err, + })?; - let wasi = module.config.wasi.as_mut().ok_or(eyre!( - "Could not inject persistent dirs into empty WASI config" + let wasi = module.config.wasi.as_mut().ok_or(InternalError( + "Could not inject persistent dirs into empty WASI config".to_string(), ))?; wasi.mapped_dirs .insert("/storage".into(), persistent_dir.to_path_buf()); @@ -1020,24 +1024,25 @@ impl ParticleAppServices { Ok(()) } - fn inject_ephemeral_dirs( + async fn inject_ephemeral_dirs( &self, module: &mut ModuleDescriptor, ephemeral_dir: &Path, - ) -> eyre::Result<()> { + ) -> Result<(), ServiceError> { let module_dir = ephemeral_dir.join(&module.import_name); - //todo: add error & make async - std::fs::create_dir_all(&module_dir)?; - - let wasi = module.config.wasi.as_mut().ok_or(eyre!( - "Could not inject ephemeral dirs into empty WASI config" + tokio::fs::create_dir_all(&module_dir) + .await + .map_err(|err| FailedToCreateDirectory { + path: module_dir.clone(), + err, + })?; + + let wasi = module.config.wasi.as_mut().ok_or(InternalError( + "Could not inject ephemeral dirs into empty WASI config".to_string(), ))?; wasi.mapped_dirs .insert("/tmp".into(), ephemeral_dir.to_path_buf()); - wasi.mapped_dirs.insert( - "/tmp/module".into(), - module_dir, - ); + wasi.mapped_dirs.insert("/tmp/module".into(), module_dir); Ok(()) } @@ -1052,21 +1057,28 @@ impl ParticleAppServices { // TODO: introduce separate errors tokio::fs::create_dir_all(&persistent_dir) .await - .map_err(|err| InternalError(err.to_string()))?; + .map_err(|err| FailedToCreateDirectory { + path: persistent_dir.clone(), + err, + })?; tokio::fs::create_dir_all(&ephemeral_dir) .await - .map_err(|err| InternalError(err.to_string()))?; + .map_err(|err| FailedToCreateDirectory { + path: ephemeral_dir.clone(), + err, + })?; let mut modules_config = self.modules.resolve_blueprint(&blueprint_id)?; - modules_config.iter_mut().for_each(|module| { + + for module in modules_config.iter_mut() { self.inject_default_wasi(module); // SAFETY: set wasi to Some in the code before calling inject_vault self.vault.inject_vault(module).unwrap(); self.inject_persistent_dirs(module, persistent_dir.as_path()) - .unwrap(); + .await?; self.inject_ephemeral_dirs(module, ephemeral_dir.as_path()) - .unwrap(); - }); + .await?; + } let app_config = AppServiceConfig { service_working_dir: persistent_dir, diff --git a/particle-services/src/error.rs b/particle-services/src/error.rs index dcaa2fb777..f55902b708 100644 --- a/particle-services/src/error.rs +++ b/particle-services/src/error.rs @@ -106,6 +106,12 @@ pub enum ServiceError { InternalError(String), #[error("Worker {worker_id} not found")] WorkerNotFound { worker_id: WorkerId }, + #[error("Failed to create directory {path}: {err}")] + FailedToCreateDirectory { + path: PathBuf, + #[source] + err: std::io::Error, + }, } impl From for ServiceError { From e0c1067f85ed2b89d5f2b0d47be4299b69252b90 Mon Sep 17 00:00:00 2001 From: folex <0xdxdy@gmail.com> Date: Fri, 23 Feb 2024 13:46:31 -0300 Subject: [PATCH 52/53] fix(deps): decider 0.6.7 --- Cargo.lock | 4 ++-- crates/system-services/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 780184ecc4..69c252513b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,9 +1770,9 @@ dependencies = [ [[package]] name = "decider-distro" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc977928aedc97953c9bd41627dd498c7cfd06e63b55b62464be45f6299287e" +checksum = "03a8de76f4a45b6c3199a96f81ea6e6cb84fa814b2dd3dbed2cdf5583b3f53ab" dependencies = [ "built 0.7.1", "fluence-spell-dtos", diff --git a/crates/system-services/Cargo.toml b/crates/system-services/Cargo.toml index 79642480c1..d1839ff7a8 100644 --- a/crates/system-services/Cargo.toml +++ b/crates/system-services/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] aqua-ipfs-distro = "=0.5.30" -decider-distro = "=0.6.6" +decider-distro = "=0.6.7" registry-distro = "=0.9.4" trust-graph-distro = "=0.4.11" From 399c47447ca374d8c61e965c5774ca4dc1c49224 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Feb 2024 19:58:50 +0300 Subject: [PATCH 53/53] fixes --- crates/service-modules/src/modules/fixture.rs | 1 + particle-execution/Cargo.toml | 2 +- particle-services/src/app_services.rs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/service-modules/src/modules/fixture.rs b/crates/service-modules/src/modules/fixture.rs index 9ce4e8cb61..51d9e73976 100644 --- a/crates/service-modules/src/modules/fixture.rs +++ b/crates/service-modules/src/modules/fixture.rs @@ -32,6 +32,7 @@ pub fn module_config(import_name: &str) -> JValue { "name": import_name, "mem_pages_count": 100, "logger_enabled": true, + "preopened_files": vec!["/tmp"], "wasi": { "envs": json!({}), "mapped_dirs": json!({}), diff --git a/particle-execution/Cargo.toml b/particle-execution/Cargo.toml index 763a1630de..40d896bd8f 100644 --- a/particle-execution/Cargo.toml +++ b/particle-execution/Cargo.toml @@ -21,4 +21,4 @@ bs58 = { workspace = true } tokio = { workspace = true, features = ["fs"] } parking_lot = { workspace = true } async-trait = { workspace = true } -eyre = { workspace = true } \ No newline at end of file +eyre = { workspace = true } diff --git a/particle-services/src/app_services.rs b/particle-services/src/app_services.rs index 69be2c3a8f..083464aaf8 100644 --- a/particle-services/src/app_services.rs +++ b/particle-services/src/app_services.rs @@ -1175,9 +1175,9 @@ mod tests { ) -> ParticleAppServices { let persistent_dir = base_dir.join("persistent"); let ephemeral_dir = base_dir.join("ephemeral"); - let vault_dir = ephemeral_dir.join("..").join("vault"); - let keypairs_dir = persistent_dir.join("..").join("keypairs"); - let workers_dir = persistent_dir.join("..").join("workers"); + let vault_dir = ephemeral_dir.join("vault"); + let keypairs_dir = persistent_dir.join("keypairs"); + let workers_dir = persistent_dir.join("workers"); let service_memory_limit = server_config::default_service_memory_limit(); let key_storage = KeyStorage::from_path(keypairs_dir.clone(), root_keypair.clone().into()) .await