diff --git a/config/Config.kdl b/config/Config.kdl new file mode 100644 index 00000000..b814987d --- /dev/null +++ b/config/Config.kdl @@ -0,0 +1,7 @@ +plugin { + backoff-interval 100000 + max-spawn-attempts 5 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 +} diff --git a/hipcheck/src/cli.rs b/hipcheck/src/cli.rs index e1891536..abcb2a1a 100644 --- a/hipcheck/src/cli.rs +++ b/hipcheck/src/cli.rs @@ -6,6 +6,7 @@ use crate::{ cache::repo::{RepoCacheDeleteScope, RepoCacheListScope, RepoCacheSort}, error::Context, error::Result, + env, hc_error, plugin::Arch, session::pm, @@ -104,6 +105,16 @@ struct PathArgs { long_help = "Path to the policy file." )] policy: Option, + + /// Path to the exec config file ADDED + #[arg( + short = 'e', + long = "exec", + global = true, + help_heading = "Path Flags", + long_help = "Path to the exec file." + )] + exec: Option, } /// Soft-deprecated arguments, to be removed in a future version. @@ -221,6 +232,11 @@ impl CliConfig { self.deprecated_args.config.as_deref() } + /// Get the path to the exec config file + pub fn exec(&self) -> Option<&Path> { + self.path_args.exec.as_deref() + } + /// Check if the `--print-home` flag was used. pub fn print_home(&self) -> bool { self.deprecated_args.print_home.unwrap_or(false) @@ -259,6 +275,8 @@ impl CliConfig { cache: hc_env_var("cache"), // For now, we do not get this from the environment, so pass a None to never update this field policy: None, + // For now, we don't get this from the environment + exec: None, }, deprecated_args: DeprecatedArgs { config: hc_env_var("config"), @@ -277,8 +295,9 @@ impl CliConfig { CliConfig { path_args: PathArgs { cache: platform_cache(), - // There is no central per-user or per-system location for the policy file, so pass a None to never update this field + // There is no central per-user or per-system location for the policy or exec file, so pass a None to never update this field policy: None, + exec: None, }, deprecated_args: DeprecatedArgs { config: platform_config(), @@ -296,6 +315,7 @@ impl CliConfig { policy: std::env::current_dir() .ok() .map(|dir| pathbuf![&dir, "Hipcheck.kdl"]), + exec: env::current_dir().ok().map(|dir| pathbuf![&dir, "config/Config.kdl"]), }, deprecated_args: DeprecatedArgs { config: dirs::home_dir().map(|dir| pathbuf![&dir, "hipcheck", "config"]), diff --git a/hipcheck/src/config.rs b/hipcheck/src/config.rs index 3b088d4d..8ceed4be 100644 --- a/hipcheck/src/config.rs +++ b/hipcheck/src/config.rs @@ -5,6 +5,7 @@ use crate::{ engine::HcEngine, error::{Context, Result}, + executor::ExecConfig, hc_error, policy::{ policy_file::{PolicyAnalysis, PolicyCategory, PolicyCategoryChild}, @@ -453,6 +454,9 @@ pub trait ConfigSource: salsa::Database { /// Returns the location of the policy file #[salsa::input] fn policy_path(&self) -> Option>; + /// Returns the input `Exec Config` struct + #[salsa::input] + fn exec_config(&self) -> Rc; /// Returns the token set in HC_GITHUB_TOKEN env var #[salsa::input] fn github_api_token(&self) -> Option>; diff --git a/hipcheck/src/engine.rs b/hipcheck/src/engine.rs index d4514d98..9f818773 100644 --- a/hipcheck/src/engine.rs +++ b/hipcheck/src/engine.rs @@ -223,15 +223,8 @@ impl HcEngineImpl { pub fn start_plugins( policy_file: &PolicyFile, plugin_cache: &HcPluginCache, + executor: PluginExecutor ) -> Result> { - let executor = PluginExecutor::new( - /* max_spawn_attempts */ 3, - /* max_conn_attempts */ 5, - /* port_range */ 40000..u16::MAX, - /* backoff_interval_micros */ 100000, - /* jitter_percent */ 10, - )?; - let current_arch = get_current_arch(); // retrieve, verify and extract all required plugins diff --git a/hipcheck/src/executor.rs b/hipcheck/src/executor.rs new file mode 100644 index 00000000..9293a07e --- /dev/null +++ b/hipcheck/src/executor.rs @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: Apache-2.0 +// reference hipcheck/src/plugin/plugin_manifest.rs +use crate::{ + error::Error, + hc_error, + util::{ + fs::read_string, + kdl::{extract_data, ParseKdlNode}, + }, +}; +use kdl::{KdlDocument, KdlNode, KdlValue}; +use std::{path::Path, str::FromStr}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PluginBackoffInterval { + /// size of the downloaded artifact, in bytes + pub micros: u64, +} + +impl PluginBackoffInterval { + #[cfg(test)] + pub fn new(micros: u64) -> Self { + Self { micros } + } +} + +impl ParseKdlNode for PluginBackoffInterval { + fn kdl_key() -> &'static str { + "backoff-interval" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let specified_duration = node.entries().first()?; + let micros = match specified_duration.value() { + // Value should be greater than 0 + KdlValue::Base10(micros) => { + let micros = *micros; + if micros.is_positive() { + micros as u64 + } else { + return None; + } + } + _ => return None, + }; + Some(PluginBackoffInterval { micros }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PluginMaxSpawnAttempts { + /// the number of spawns to attempt + pub attempts: usize, +} + +impl PluginMaxSpawnAttempts { + #[cfg(test)] + pub fn new(attempts: usize) -> Self { + Self { attempts } + } +} + +impl ParseKdlNode for PluginMaxSpawnAttempts { + fn kdl_key() -> &'static str { + "max-spawn-attempts" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let specified_attempts = node.entries().first()?; + let attempts = match specified_attempts.value() { + // Value should be greater than 0 + KdlValue::Base10(attempts) => { + let attempts = *attempts; + if attempts.is_positive() { + attempts as usize + } else { + return None; + } + } + _ => return None, + }; + Some(PluginMaxSpawnAttempts { attempts }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PluginMaxConnectionAttempts { + /// the number of spawns to attempt + pub attempts: usize, +} + +impl PluginMaxConnectionAttempts { + #[cfg(test)] + pub fn new(attempts: usize) -> Self { + Self { attempts } + } +} + +impl ParseKdlNode for PluginMaxConnectionAttempts { + fn kdl_key() -> &'static str { + "max-conn-attempts" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let specified_attempts = node.entries().first()?; + let attempts = match specified_attempts.value() { + // Value should be greater than 0 + KdlValue::Base10(attempts) => { + let attempts = *attempts; + if attempts.is_positive() { + attempts as usize + } else { + return None; + } + } + _ => return None, + }; + Some(PluginMaxConnectionAttempts { attempts }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PluginJitterPercent { + /// the number of spawns to attempt + pub percent: u8, +} + +impl PluginJitterPercent { + #[cfg(test)] + pub fn new(percent: u8) -> Self { + Self { percent } + } +} + +impl ParseKdlNode for PluginJitterPercent { + fn kdl_key() -> &'static str { + "jitter-percent" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let specified_percentage = node.entries().first()?; + let percent = match specified_percentage.value() { + // Value should be greater than 0 + KdlValue::Base10(percent) => { + let percent = *percent; + if percent.is_positive() { + percent as u8 + } else { + return None; + } + } + _ => return None, + }; + Some(PluginJitterPercent { percent }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PluginMsgBufferSize { + /// size of the buffer for the grpc buffer + pub size: usize, +} + +impl PluginMsgBufferSize { + #[cfg(test)] + pub fn new(size: usize) -> Self { + Self { size } + } +} + +impl ParseKdlNode for PluginMsgBufferSize { + fn kdl_key() -> &'static str { + "grpc-msg-buffer-size" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let specified_size = node.entries().first()?; + let size = match specified_size.value() { + // Value should be greater than 0 + KdlValue::Base10(size) => { + let size = *size; + if size.is_positive() { + size as usize + } else { + return None; + } + } + _ => return None, + }; + Some(PluginMsgBufferSize { size }) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PluginConfig { + pub backoff: PluginBackoffInterval, + pub max_spawn: PluginMaxSpawnAttempts, + pub max_conn: PluginMaxConnectionAttempts, + pub jitter: PluginJitterPercent, + pub grpc_buffer: PluginMsgBufferSize, +} + +impl PluginConfig { + #[cfg(test)] + pub fn new( + backoff: PluginBackoffInterval, + max_spawn: PluginMaxSpawnAttempts, + max_conn: PluginMaxConnectionAttempts, + jitter: PluginJitterPercent, + grpc_buffer: PluginMsgBufferSize, + ) -> Self { + Self { + backoff, + max_spawn, + max_conn, + jitter, + grpc_buffer, + } + } +} + +impl ParseKdlNode for PluginConfig { + fn kdl_key() -> &'static str { + "plugin" + } + + fn parse_node(node: &KdlNode) -> Option { + if node.name().to_string().as_str() != Self::kdl_key() { + return None; + } + let nodes = node.children()?.nodes(); + + // extract the configured plugin data from the child + let backoff: PluginBackoffInterval = extract_data(nodes)?; + let max_spawn: PluginMaxSpawnAttempts = extract_data(nodes)?; + let max_conn: PluginMaxConnectionAttempts = extract_data(nodes)?; + let jitter: PluginJitterPercent = extract_data(nodes)?; + let grpc_buffer: PluginMsgBufferSize = extract_data(nodes)?; + + Some(Self { + backoff, + max_spawn, + max_conn, + jitter, + grpc_buffer, + }) + } + + // add to_kdl(&self) & to_kdl_formatted_string from plugin manifest +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExecConfig { + pub plugin_data: PluginConfig, + // Any new configurable data forms can be added here +} + +impl ExecConfig { + pub fn from_file

(path: P) -> Result + where + P: AsRef, + { + Self::from_str(&read_string(path)?) + } +} + +impl FromStr for ExecConfig { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let document = KdlDocument::from_str(s) + .map_err(|e| hc_error!("Error parsing exec config file: {}", e))?; + let nodes = document.nodes(); + let plugin_data: PluginConfig = extract_data(nodes).unwrap(); + // Future config nodes will be here + Ok(Self { plugin_data }) + } +} + +#[cfg(test)] +mod test { + use std::path::PathBuf; + + use super::*; + use pathbuf::pathbuf; + + #[test] + fn test_parsing_plugin_backoff_interval() { + let data = "backoff-interval 100000"; + let node = KdlNode::from_str(data).unwrap(); + assert_eq!( + PluginBackoffInterval::new(100000), + PluginBackoffInterval::parse_node(&node).unwrap() + ) + } + + #[test] + fn test_parsing_plugin_max_spawns() { + let data = "max-spawn-attempts 3"; + let node = KdlNode::from_str(data).unwrap(); + assert_eq!( + PluginMaxSpawnAttempts::new(3), + PluginMaxSpawnAttempts::parse_node(&node).unwrap() + ) + } + + #[test] + fn test_parsing_plugin_max_connections() { + let data = "max-conn-attempts 5"; + let node = KdlNode::from_str(data).unwrap(); + assert_eq!( + PluginMaxConnectionAttempts::new(5), + PluginMaxConnectionAttempts::parse_node(&node).unwrap() + ) + } + + #[test] + fn test_parsing_plugin_jitter_percent() { + let data = "jitter-percent 10"; + let node = KdlNode::from_str(data).unwrap(); + assert_eq!( + PluginJitterPercent::new(10), + PluginJitterPercent::parse_node(&node).unwrap() + ) + } + + #[test] + fn test_parsing_plugin_buffer_size() { + let data = "grpc-msg-buffer-size 10"; + let node = KdlNode::from_str(data).unwrap(); + assert_eq!( + PluginMsgBufferSize::new(10), + PluginMsgBufferSize::parse_node(&node).unwrap() + ) + } + + #[test] + fn test_parsing_plugin_config() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 +}"#; + let node = KdlNode::from_str(data).unwrap(); + let backoff = PluginBackoffInterval::new(100000); + let max_spawn = PluginMaxSpawnAttempts::new(3); + let max_conn = PluginMaxConnectionAttempts::new(5); + let jitter = PluginJitterPercent::new(10); + let grpc_buffer = PluginMsgBufferSize::new(10); + + let expected = PluginConfig::new(backoff, max_spawn, max_conn, jitter, grpc_buffer); + + assert_eq!(expected, PluginConfig::parse_node(&node).unwrap()) + } + + #[test] + fn test_parsing_plugin_config_backoff() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let node = KdlNode::from_str(data).unwrap(); + let parsed_node = PluginConfig::parse_node(&node).unwrap(); + + assert_eq!(parsed_node.backoff.micros, 100000); + } + + #[test] + fn test_parsing_plugin_config_max_spawn() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let node = KdlNode::from_str(data).unwrap(); + let parsed_node = PluginConfig::parse_node(&node).unwrap(); + + assert_eq!(parsed_node.max_spawn.attempts, 3); + } + + #[test] + fn test_parsing_plugin_config_max_conn() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let node = KdlNode::from_str(data).unwrap(); + let parsed_node = PluginConfig::parse_node(&node).unwrap(); + + assert_ne!(parsed_node.max_conn.attempts, 3); + } + + #[test] + fn test_parsing_plugin_config_jitter() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let node = KdlNode::from_str(data).unwrap(); + let parsed_node = PluginConfig::parse_node(&node).unwrap(); + + assert_eq!(parsed_node.jitter.percent, 10); + } + + #[test] + fn test_parsing_plugin_config_buffer() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let node = KdlNode::from_str(data).unwrap(); + let parsed_node = PluginConfig::parse_node(&node).unwrap(); + + assert_eq!(parsed_node.grpc_buffer.size, 10); + } + + #[test] + fn test_parsing_exec_config_from_str() { + let data = r#"plugin { + backoff-interval 100000 + max-spawn-attempts 3 + max-conn-attempts 5 + jitter-percent 10 + grpc-msg-buffer-size 10 + }"#; + let exec_config = ExecConfig::from_str(data).unwrap(); + assert_eq!(exec_config.plugin_data.backoff.micros, 100000); + assert_eq!(exec_config.plugin_data.max_spawn.attempts, 3); + assert_eq!(exec_config.plugin_data.max_conn.attempts, 5); + assert_eq!(exec_config.plugin_data.jitter.percent, 10); + assert_eq!(exec_config.plugin_data.grpc_buffer.size, 10); + } + + #[test] + fn test_read_exec_config_file() { + let root = workspace_dir(); + let path = pathbuf![&root, "config", "Config.kdl"]; + let config = ExecConfig::from_file(path); + assert!(config.is_ok()) + } + + // Adapted from https://stackoverflow.com/a/74942075 + // Licensed CC BY-SA 4.0 + fn workspace_dir() -> PathBuf { + let output = std::process::Command::new(env!("CARGO")) + .arg("locate-project") + .arg("--workspace") + .arg("--message-format=plain") + .output() + .unwrap() + .stdout; + let cargo_path = Path::new(std::str::from_utf8(&output).unwrap().trim()); + cargo_path.parent().unwrap().to_path_buf() + } + + #[test] + fn test_parsing_exec_config_from_file() { + let root = workspace_dir(); + let path = pathbuf![&root, "config", "Config.kdl"]; + let config = ExecConfig::from_file(path).unwrap(); + + assert_eq!(config.plugin_data.backoff.micros, 100000); + assert_eq!(config.plugin_data.max_spawn.attempts, 3); + assert_eq!(config.plugin_data.max_conn.attempts, 5); + assert_eq!(config.plugin_data.jitter.percent, 10); + assert_eq!(config.plugin_data.grpc_buffer.size, 10); + } + +} diff --git a/hipcheck/src/main.rs b/hipcheck/src/main.rs index b3304ed0..cb0b27f2 100644 --- a/hipcheck/src/main.rs +++ b/hipcheck/src/main.rs @@ -7,6 +7,7 @@ mod cli; mod config; mod engine; mod error; +mod executor; mod init; mod plugin; mod policy; @@ -26,6 +27,7 @@ use crate::{ cli::Format, config::{normalized_unresolved_analysis_tree_from_policy, Config}, error::{Context as _, Error, Result}, + executor::ExecConfig, plugin::{try_set_arch, Plugin, PluginExecutor, PluginWithConfig}, policy::{config_to_policy, PolicyFile}, report::report_builder::{build_report, Report}, @@ -91,7 +93,7 @@ fn main() -> ExitCode { Some(FullCommands::Ready) => cmd_ready(&config), Some(FullCommands::Update(args)) => cmd_update(&args), Some(FullCommands::Cache(args)) => return cmd_cache(args, &config), - Some(FullCommands::Plugin(args)) => cmd_plugin(args), + Some(FullCommands::Plugin(args)) => cmd_plugin(args, &config), Some(FullCommands::PrintConfig) => cmd_print_config(config.config()), Some(FullCommands::PrintCache) => cmd_print_home(config.cache()), Some(FullCommands::Scoring) => { @@ -132,6 +134,7 @@ fn cmd_check(args: &CheckArgs, config: &CliConfig) -> ExitCode { config.config().map(ToOwned::to_owned), config.cache().map(ToOwned::to_owned), config.policy().map(ToOwned::to_owned), + config.exec().map(ToOwned::to_owned), config.format(), ); @@ -484,7 +487,7 @@ fn check_policy_path(config: &CliConfig) -> StdResult { Ok(path.to_owned()) } -fn cmd_plugin(args: PluginArgs) { +fn cmd_plugin(args: PluginArgs, config: &CliConfig) { use crate::engine::{async_query, HcEngine, HcEngineImpl}; use std::sync::Arc; use tokio::task::JoinSet; @@ -503,12 +506,22 @@ fn cmd_plugin(args: PluginArgs) { working_dir: working_dir.clone(), entrypoint: entrypoint2.display().to_string(), }; + let exec_config = if let Some(p) = config.exec() { + ExecConfig::from_file(p) + .context("Failed to load the exec config. Please make sure the exec config file is in the provided location and is formatted correctly.") + } else { + Err(hc_error!("No exec file found. Please provide an exec config file before running Hipcheck.")) + }; + + let plugin_data = exec_config.unwrap().plugin_data; + let plugin_executor = PluginExecutor::new( - /* max_spawn_attempts */ 3, - /* max_conn_attempts */ 5, + /* max_spawn_attempts */ plugin_data.max_spawn.attempts, + /* max_conn_attempts */ plugin_data.max_conn.attempts, /* port_range */ 40000..u16::MAX, - /* backoff_interval_micros */ 100000, - /* jitter_percent */ 10, + /* backoff_interval_micros */ plugin_data.backoff.micros, + /* jitter_percent */ plugin_data.jitter.percent, + /*grpc_buffer*/ plugin_data.grpc_buffer.size ) .unwrap(); let engine = match HcEngineImpl::new( @@ -789,10 +802,11 @@ fn run( config_path: Option, home_dir: Option, policy_path: Option, + exec_path: Option, format: Format, ) -> Result { // Initialize the session. - let session = match Session::new(&target, config_path, home_dir, policy_path, format) { + let session = match Session::new(&target, config_path, home_dir, policy_path, exec_path, format) { Ok(session) => session, Err(err) => return Err(err), }; diff --git a/hipcheck/src/plugin/manager.rs b/hipcheck/src/plugin/manager.rs index 142ea0ca..e0c1c576 100644 --- a/hipcheck/src/plugin/manager.rs +++ b/hipcheck/src/plugin/manager.rs @@ -18,6 +18,7 @@ pub struct PluginExecutor { port_range: Range, backoff_interval: Duration, jitter_percent: u8, + grpc_buffer: usize } impl PluginExecutor { pub fn new( @@ -26,6 +27,7 @@ impl PluginExecutor { port_range: Range, backoff_interval_micros: u64, jitter_percent: u8, + grpc_buffer: usize ) -> Result { if jitter_percent > 100 { return Err(hc_error!( @@ -42,6 +44,7 @@ impl PluginExecutor { port_range, backoff_interval, jitter_percent, + grpc_buffer }) } @@ -190,6 +193,7 @@ impl PluginExecutor { port, grpc, proc, + grpc_buffer: self.grpc_buffer }); } Err(hc_error!( diff --git a/hipcheck/src/plugin/types.rs b/hipcheck/src/plugin/types.rs index cc0e9b52..2c7bcb43 100644 --- a/hipcheck/src/plugin/types.rs +++ b/hipcheck/src/plugin/types.rs @@ -160,6 +160,9 @@ pub struct PluginContext { /// The child process in which the plugin is running. pub proc: Child, + + /// The size of the gRPC buffer + pub grpc_buffer: usize, } // Redefinition of `grpc` field's functions with more useful types, additional @@ -307,8 +310,7 @@ impl PluginContext { let opt_explain_default_query = self.explain_default_query().await?; - // TODO: Make the size of this channel configurable. - let (tx, out_rx) = mpsc::channel::(10); + let (tx, out_rx) = mpsc::channel::(self.grpc_buffer); let rx = self.initiate_query_protocol(out_rx).await?; Ok(PluginTransport { diff --git a/hipcheck/src/session/mod.rs b/hipcheck/src/session/mod.rs index 1ae5fcae..ed867055 100644 --- a/hipcheck/src/session/mod.rs +++ b/hipcheck/src/session/mod.rs @@ -14,8 +14,10 @@ use crate::{ }, engine::{start_plugins, HcEngine, HcEngineStorage}, error::{Context as _, Error, Result}, + executor::ExecConfig, hc_error, policy::{config_to_policy, PolicyFile}, + PluginExecutor, report::{ReportParams, ReportParamsStorage}, score::ScoringProviderStorage, session::{ @@ -92,6 +94,7 @@ impl Session { config_path: Option, home_dir: Option, policy_path: Option, + exec_path: Option, format: Format, ) -> StdResult { /*=================================================================== @@ -158,6 +161,17 @@ impl Session { // Force eval the risk policy expr - wouldn't be necessary if the PolicyFile parsed let _ = session.risk_policy()?; + /*=================================================================== + * Load the Exec Configuration + *-----------------------------------------------------------------*/ + let exec = + match load_exec_config(exec_path.as_deref()) { + Ok(config) => config, + Err(err) => return Err(err) + }; + + session.set_exec_config(Rc::new(exec)); + /*=================================================================== * Resolving the Hipcheck home. *-----------------------------------------------------------------*/ @@ -212,7 +226,19 @@ impl Session { // equal, and the idea of memoizing/invalidating it does not make sense. // Thus, we will do the plugin startup here. let policy = session.policy(); - let core = start_plugins(policy.as_ref(), &plugin_cache)?; + + let exec_config = session.exec_config(); + let plugin_data = &exec_config.plugin_data; + + let executor = PluginExecutor::new( + /* max_spawn_attempts */ plugin_data.max_spawn.attempts, + /* max_conn_attempts */ plugin_data.max_conn.attempts, + /* port_range */ 40000..u16::MAX, + /* backoff_interval_micros */ plugin_data.backoff.micros, + /* jitter_percent */ plugin_data.jitter.percent, + /*grpc_buffer*/ plugin_data.grpc_buffer.size + )?; + let core = start_plugins(policy.as_ref(), &plugin_cache, executor)?; session.set_core(core); Ok(session) @@ -270,13 +296,37 @@ pub fn load_policy_and_data(policy_path: Option<&Path>) -> Result<(PolicyFile, P // Load the policy file. let policy = PolicyFile::load_from(valid_policy_path) - .context("Failed to load policy. Plase make sure the policy file is in the provided location and is formatted correctly.")?; + .context("Failed to load policy. Please make sure the policy file is in the provided location and is formatted correctly.")?; phase.finish_successful(); Ok((policy, valid_policy_path.to_path_buf())) } +fn load_exec_config(exec_path: Option<&Path>) -> Result { + // Start the phase QUESTION + let phase = SpinnerPhase::start("loading exec config"); + // Increment the phase into the "running" stage. + phase.inc(); + // Set the spinner phase to tick constantly, 10 times a second. + phase.enable_steady_tick(Duration::from_millis(100)); + + // Resolve the path to the exec config file. + let valid_path = exec_path.ok_or_else(|| { + hc_error!( + "Failed to load exec config. Please make sure the path set by the --exec flag exists." + ) + })?; + + // Load the exec config file + let exec_config = ExecConfig::from_file(valid_path) + .context("Failed to load the exec config. Please make sure the exec config file is in the provided location and is formatted correctly.")?; + + phase.finish_successful(); + + Ok(exec_config) +} + fn load_target(seed: &TargetSeed, home: &Path) -> Result { // Resolve the source specifier into an actual source. let phase_desc = match seed.kind {