diff --git a/v2/robotmk/src/bin/scheduler/environment.rs b/v2/robotmk/src/bin/scheduler/environment.rs index 1cb9885b..aa663558 100644 --- a/v2/robotmk/src/bin/scheduler/environment.rs +++ b/v2/robotmk/src/bin/scheduler/environment.rs @@ -1,9 +1,9 @@ -use super::internal_config::{GlobalConfig, Suite}; +use super::internal_config::{ + apply_current_settings, Environment, GlobalConfig, RCCEnvironment, Suite, +}; use super::logging::log_and_return_error; use robotmk::child_process_supervisor::{ChildProcessOutcome, ChildProcessSupervisor, StdioPaths}; use robotmk::command_spec::CommandSpec; -use robotmk::config::EnvironmentConfig; -use robotmk::environment::ResultCode; use robotmk::results::{BuildOutcome, BuildStates, EnvironmentBuildStage}; use robotmk::lock::Locker; @@ -33,7 +33,7 @@ pub fn build_environments(global_config: &GlobalConfig, suites: Vec) -> R for suite in suites.into_iter() { let outcome = build_environment( &suite.id, - suite.environment.build_instructions(), + &suite.environment, &global_config.cancellation_token, &mut build_stage_reporter, &env_building_stdio_directory, @@ -50,40 +50,55 @@ pub fn build_environments(global_config: &GlobalConfig, suites: Vec) -> R fn build_environment( id: &str, - instructions: Option, + environment: &Environment, cancellation_token: &CancellationToken, build_stage_reporter: &mut BuildStageReporter, stdio_directory: &Utf8Path, ) -> Result { - let Some(instructions) = instructions else { + let Environment::Rcc(environment) = environment else { let outcome = BuildOutcome::NotNeeded; debug!("Nothing to do for suite {}", id); build_stage_reporter.update(id, EnvironmentBuildStage::Complete(outcome.clone()))?; return Ok(outcome); }; info!("Building environment for suite {}", id); + let command_spec = create_build_command(environment); + let supervisor = ChildProcessSupervisor { + command_spec: &command_spec, + stdio_paths: Some(StdioPaths { + stdout: stdio_directory.join(format!("{}.stdout", id)), + stderr: stdio_directory.join(format!("{}.stderr", id)), + }), + timeout: environment.build_timeout, + cancellation_token, + }; let start_time = Utc::now(); build_stage_reporter.update( id, EnvironmentBuildStage::InProgress(start_time.timestamp()), )?; - let outcome = run_environment_build( - ChildProcessSupervisor { - command_spec: &instructions.command_spec, - stdio_paths: Some(StdioPaths { - stdout: stdio_directory.join(format!("{}.stdout", id)), - stderr: stdio_directory.join(format!("{}.stderr", id)), - }), - timeout: instructions.timeout, - cancellation_token, - }, - start_time, - )?; + let outcome = run_build_command(supervisor, start_time)?; build_stage_reporter.update(id, EnvironmentBuildStage::Complete(outcome.clone()))?; Ok(outcome) } -fn run_environment_build( +fn create_build_command(environment: &RCCEnvironment) -> CommandSpec { + let mut command_spec = CommandSpec::new(&environment.binary_path); + command_spec + .add_argument("holotree") + .add_argument("variables") + .add_argument("--json"); + apply_current_settings( + &environment.robot_yaml_path, + &environment.controller, + &environment.space, + environment.env_json_path.as_deref(), + &mut command_spec, + ); + command_spec +} + +fn run_build_command( build_process_supervisor: ChildProcessSupervisor, reference_timestamp_for_duration: DateTime, ) -> Result { @@ -115,225 +130,39 @@ fn run_environment_build( } } -#[derive(Clone)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub enum Environment { - System(SystemEnvironment), - Rcc(RCCEnvironment), -} - -#[derive(Clone)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct SystemEnvironment {} - -#[derive(Clone)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct RCCEnvironment { - pub binary_path: Utf8PathBuf, - pub robot_yaml_path: Utf8PathBuf, - pub controller: String, - pub space: String, - pub build_timeout: u64, - pub env_json_path: Option, -} - -impl Environment { - pub fn new( - suite_id: &str, - rcc_binary_path: &Utf8Path, - environment_config: &EnvironmentConfig, - ) -> Self { - match environment_config { - EnvironmentConfig::System => Self::System(SystemEnvironment {}), - EnvironmentConfig::Rcc(rcc_environment_config) => Self::Rcc(RCCEnvironment { - binary_path: rcc_binary_path.to_path_buf(), - robot_yaml_path: rcc_environment_config.robot_yaml_path.clone(), - controller: String::from("robotmk"), - space: suite_id.to_string(), - build_timeout: rcc_environment_config.build_timeout, - env_json_path: rcc_environment_config.env_json_path.clone(), - }), - } - } - - pub fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { - match self { - Self::System(system_environment) => system_environment.wrap(command_spec), - Self::Rcc(rcc_environment) => rcc_environment.wrap(command_spec), - } - } - - pub fn create_result_code(&self, exit_code: i32) -> ResultCode { - match self { - Self::System(_) => SystemEnvironment::create_result_code(exit_code), - Self::Rcc(_) => RCCEnvironment::create_result_code(exit_code), - } - } - - fn build_instructions(&self) -> Option { - match self { - Self::System(system_environment) => system_environment.build_instructions(), - Self::Rcc(rcc_environment) => rcc_environment.build_instructions(), - } - } -} - -struct BuildInstructions { - command_spec: CommandSpec, - timeout: u64, -} - -impl SystemEnvironment { - fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { - command_spec - } - - fn create_result_code(exit_code: i32) -> ResultCode { - if exit_code == 0 { - return ResultCode::AllTestsPassed; - } - ResultCode::RobotCommandFailed - } - - fn build_instructions(&self) -> Option { - None - } +struct BuildStageReporter<'a> { + build_states: HashMap, + path: Utf8PathBuf, + locker: &'a Locker, } -impl RCCEnvironment { - fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { - let mut wrapped_spec = CommandSpec::new(&self.binary_path); - wrapped_spec - .add_argument("task") - .add_argument("script") - .add_argument("--no-build"); - self.apply_current_settings(&mut wrapped_spec); - wrapped_spec - .add_argument("--") - .add_argument(command_spec.executable) - .add_arguments(command_spec.arguments); - wrapped_spec - } - - fn create_result_code(exit_code: i32) -> ResultCode { - match exit_code { - 0 => ResultCode::AllTestsPassed, - 10 => ResultCode::RobotCommandFailed, - _ => ResultCode::EnvironmentFailed, - } - } - - fn build_instructions(&self) -> Option { - let mut command_spec = CommandSpec::new(&self.binary_path); - self.apply_current_settings( - command_spec - .add_argument("holotree") - .add_argument("variables") - .add_argument("--json"), - ); - Some(BuildInstructions { - command_spec, - timeout: self.build_timeout, +impl<'a> BuildStageReporter<'a> { + pub fn new<'c>( + ids: impl Iterator, + results_directory: &Utf8Path, + locker: &'a Locker, + ) -> Result> { + let build_states: HashMap<_, _> = ids + .map(|id| (id.to_string(), EnvironmentBuildStage::Pending)) + .collect(); + let path = results_directory.join("environment_build_states.json"); + BuildStates(&build_states).write(&path, locker)?; + Ok(Self { + build_states, + path, + locker, }) } - fn apply_current_settings(&self, command_spec: &mut CommandSpec) { - command_spec - .add_argument("--robot") - .add_argument(&self.robot_yaml_path) - .add_argument("--controller") - .add_argument(&self.controller) - .add_argument("--space") - .add_argument(&self.space); - if let Some(env_json_path) = &self.env_json_path { - command_spec - .add_argument("--environment") - .add_argument(env_json_path); - } + pub fn update(&mut self, suite_id: &str, build_status: EnvironmentBuildStage) -> Result<()> { + self.build_states.insert(suite_id.into(), build_status); + BuildStates(&self.build_states).write(&self.path, self.locker) } } #[cfg(test)] mod tests { use super::*; - use robotmk::config::RCCEnvironmentConfig; - - #[test] - fn environment_from_system_config() { - assert!( - Environment::new("my_suite", "/bin/rcc".into(), &EnvironmentConfig::System) - .build_instructions() - .is_none() - ) - } - - #[test] - fn environment_from_rcc_config() { - assert!(Environment::new( - "my_suite", - "/bin/rcc".into(), - &EnvironmentConfig::Rcc(RCCEnvironmentConfig { - robot_yaml_path: Utf8PathBuf::from("/a/b/c/robot.yaml"), - build_timeout: 60, - env_json_path: None, - }) - ) - .build_instructions() - .is_some()) - } - - fn command_spec_for_wrap() -> CommandSpec { - let mut command_spec = CommandSpec::new("C:\\x\\y\\z.exe"); - command_spec - .add_argument("arg1") - .add_argument("--flag") - .add_argument("--option") - .add_argument("option_value"); - command_spec - } - - #[test] - fn test_system_wrap() { - assert_eq!( - SystemEnvironment {}.wrap(command_spec_for_wrap()), - command_spec_for_wrap() - ); - } - - #[test] - fn test_rcc_wrap() { - let mut expected = CommandSpec::new("C:\\bin\\z.exe"); - expected - .add_argument("task") - .add_argument("script") - .add_argument("--no-build") - .add_argument("--robot") - .add_argument("C:\\my_suite\\robot.yaml") - .add_argument("--controller") - .add_argument("robotmk") - .add_argument("--space") - .add_argument("my_suite") - .add_argument("--environment") - .add_argument("C:\\my_suite\\env.json") - .add_argument("--") - .add_argument("C:\\x\\y\\z.exe") - .add_argument("arg1") - .add_argument("--flag") - .add_argument("--option") - .add_argument("option_value"); - assert_eq!( - RCCEnvironment { - binary_path: Utf8PathBuf::from("C:\\bin\\z.exe"), - robot_yaml_path: Utf8PathBuf::from("C:\\my_suite\\robot.yaml"), - controller: String::from("robotmk"), - space: String::from("my_suite"), - build_timeout: 600, - env_json_path: Some("C:\\my_suite\\env.json".into()) - } - .wrap(command_spec_for_wrap()), - expected - ); - } #[test] fn rcc_build_command() { @@ -350,48 +179,15 @@ mod tests { .add_argument("my_suite"); assert_eq!( - RCCEnvironment { + create_build_command(&RCCEnvironment { binary_path: Utf8PathBuf::from("/bin/rcc"), robot_yaml_path: Utf8PathBuf::from("/a/b/c/robot.yaml"), controller: String::from("robotmk"), space: String::from("my_suite"), build_timeout: 123, env_json_path: None, - } - .build_instructions() - .unwrap() - .command_spec, + }), expected ) } } - -struct BuildStageReporter<'a> { - build_states: HashMap, - path: Utf8PathBuf, - locker: &'a Locker, -} - -impl<'a> BuildStageReporter<'a> { - pub fn new<'c>( - ids: impl Iterator, - results_directory: &Utf8Path, - locker: &'a Locker, - ) -> Result> { - let build_states: HashMap<_, _> = ids - .map(|id| (id.to_string(), EnvironmentBuildStage::Pending)) - .collect(); - let path = results_directory.join("environment_build_states.json"); - BuildStates(&build_states).write(&path, locker)?; - Ok(Self { - build_states, - path, - locker, - }) - } - - pub fn update(&mut self, suite_id: &str, build_status: EnvironmentBuildStage) -> Result<()> { - self.build_states.insert(suite_id.into(), build_status); - BuildStates(&self.build_states).write(&self.path, self.locker) - } -} diff --git a/v2/robotmk/src/bin/scheduler/internal_config.rs b/v2/robotmk/src/bin/scheduler/internal_config.rs index 08e0f195..ac8549c6 100644 --- a/v2/robotmk/src/bin/scheduler/internal_config.rs +++ b/v2/robotmk/src/bin/scheduler/internal_config.rs @@ -1,14 +1,14 @@ -use crate::environment::Environment; use crate::rf::robot::Robot; use crate::sessions::session::Session; +use robotmk::command_spec::CommandSpec; +use robotmk::config::EnvironmentConfig; +use robotmk::config::{Config, RCCConfig, WorkingDirectoryCleanupConfig}; +use robotmk::environment::ResultCode; +use robotmk::lock::Locker; use robotmk::results::suite_results_directory; -use robotmk::{ - config::{Config, RCCConfig, WorkingDirectoryCleanupConfig}, - lock::Locker, - section::Host, -}; +use robotmk::section::Host; -use camino::Utf8PathBuf; +use camino::{Utf8Path, Utf8PathBuf}; use tokio_util::sync::CancellationToken; pub struct GlobalConfig { @@ -88,14 +88,188 @@ pub fn sort_suites_by_id(suites: &mut [Suite]) { suites.sort_by_key(|suite| suite.id.to_string()); } +#[derive(Clone)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub enum Environment { + System(SystemEnvironment), + Rcc(RCCEnvironment), +} + +#[derive(Clone)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct SystemEnvironment {} + +#[derive(Clone)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct RCCEnvironment { + pub binary_path: Utf8PathBuf, + pub robot_yaml_path: Utf8PathBuf, + pub controller: String, + pub space: String, + pub build_timeout: u64, + pub env_json_path: Option, +} + +impl Environment { + pub fn new( + suite_id: &str, + rcc_binary_path: &Utf8Path, + environment_config: &EnvironmentConfig, + ) -> Self { + match environment_config { + EnvironmentConfig::System => Self::System(SystemEnvironment {}), + EnvironmentConfig::Rcc(rcc_environment_config) => Self::Rcc(RCCEnvironment { + binary_path: rcc_binary_path.to_path_buf(), + robot_yaml_path: rcc_environment_config.robot_yaml_path.clone(), + controller: String::from("robotmk"), + space: suite_id.to_string(), + build_timeout: rcc_environment_config.build_timeout, + env_json_path: rcc_environment_config.env_json_path.clone(), + }), + } + } + + pub fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { + match self { + Self::System(system_environment) => system_environment.wrap(command_spec), + Self::Rcc(rcc_environment) => rcc_environment.wrap(command_spec), + } + } + + pub fn create_result_code(&self, exit_code: i32) -> ResultCode { + match self { + Self::System(_) => SystemEnvironment::create_result_code(exit_code), + Self::Rcc(_) => RCCEnvironment::create_result_code(exit_code), + } + } +} + +impl SystemEnvironment { + fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { + command_spec + } + + fn create_result_code(exit_code: i32) -> ResultCode { + if exit_code == 0 { + return ResultCode::AllTestsPassed; + } + ResultCode::RobotCommandFailed + } +} + +impl RCCEnvironment { + fn wrap(&self, command_spec: CommandSpec) -> CommandSpec { + let mut wrapped_spec = CommandSpec::new(&self.binary_path); + wrapped_spec + .add_argument("task") + .add_argument("script") + .add_argument("--no-build"); + apply_current_settings( + &self.robot_yaml_path, + &self.controller, + &self.space, + self.env_json_path.as_deref(), + &mut wrapped_spec, + ); + wrapped_spec + .add_argument("--") + .add_argument(command_spec.executable) + .add_arguments(command_spec.arguments); + wrapped_spec + } + + fn create_result_code(exit_code: i32) -> ResultCode { + match exit_code { + 0 => ResultCode::AllTestsPassed, + 10 => ResultCode::RobotCommandFailed, + _ => ResultCode::EnvironmentFailed, + } + } +} + +pub fn apply_current_settings( + robot_yaml_path: &Utf8Path, + controller: &str, + space: &str, + env_json_path: Option<&Utf8Path>, + command_spec: &mut CommandSpec, +) { + command_spec + .add_argument("--robot") + .add_argument(robot_yaml_path) + .add_argument("--controller") + .add_argument(controller) + .add_argument("--space") + .add_argument(space); + if let Some(env_json_path) = &env_json_path { + command_spec + .add_argument("--environment") + .add_argument(env_json_path); + } +} + #[cfg(test)] mod tests { use super::*; - use crate::environment::{RCCEnvironment, SystemEnvironment}; + use robotmk::config::RCCEnvironmentConfig; + + fn command_spec_for_wrap() -> CommandSpec { + let mut command_spec = CommandSpec::new("C:\\x\\y\\z.exe"); + command_spec + .add_argument("arg1") + .add_argument("--flag") + .add_argument("--option") + .add_argument("option_value"); + command_spec + } + + #[test] + fn test_system_wrap() { + assert_eq!( + SystemEnvironment {}.wrap(command_spec_for_wrap()), + command_spec_for_wrap() + ); + } + + #[test] + fn test_rcc_wrap() { + let mut expected = CommandSpec::new("C:\\bin\\z.exe"); + expected + .add_argument("task") + .add_argument("script") + .add_argument("--no-build") + .add_argument("--robot") + .add_argument("C:\\my_suite\\robot.yaml") + .add_argument("--controller") + .add_argument("robotmk") + .add_argument("--space") + .add_argument("my_suite") + .add_argument("--environment") + .add_argument("C:\\my_suite\\env.json") + .add_argument("--") + .add_argument("C:\\x\\y\\z.exe") + .add_argument("arg1") + .add_argument("--flag") + .add_argument("--option") + .add_argument("option_value"); + assert_eq!( + RCCEnvironment { + binary_path: Utf8PathBuf::from("C:\\bin\\z.exe"), + robot_yaml_path: Utf8PathBuf::from("C:\\my_suite\\robot.yaml"), + controller: String::from("robotmk"), + space: String::from("my_suite"), + build_timeout: 600, + env_json_path: Some("C:\\my_suite\\env.json".into()) + } + .wrap(command_spec_for_wrap()), + expected + ); + } + use crate::sessions::session::{CurrentSession, UserSession}; use robotmk::config::{ - EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, RCCProfileConfig, RetryStrategy, - RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig, + EnvironmentConfig, ExecutionConfig, RCCProfileConfig, RetryStrategy, RobotFrameworkConfig, + SessionConfig, SuiteConfig, UserSessionConfig, }; use std::collections::HashMap; diff --git a/v2/robotmk/src/bin/scheduler/rf/rebot.rs b/v2/robotmk/src/bin/scheduler/rf/rebot.rs index 60b9fb4c..1a83562f 100644 --- a/v2/robotmk/src/bin/scheduler/rf/rebot.rs +++ b/v2/robotmk/src/bin/scheduler/rf/rebot.rs @@ -1,5 +1,5 @@ use super::robot::PYTHON_EXECUTABLE; -use crate::environment::Environment; +use crate::internal_config::Environment; use robotmk::command_spec::CommandSpec; use robotmk::environment::ResultCode; use robotmk::results::{RebotOutcome, RebotResult}; diff --git a/v2/robotmk/src/bin/scheduler/setup/rcc.rs b/v2/robotmk/src/bin/scheduler/setup/rcc.rs index 2f40c1e7..91c535a1 100644 --- a/v2/robotmk/src/bin/scheduler/setup/rcc.rs +++ b/v2/robotmk/src/bin/scheduler/setup/rcc.rs @@ -1,6 +1,5 @@ use super::icacls::run_icacls_command; -use crate::environment::Environment; -use crate::internal_config::{sort_suites_by_id, GlobalConfig, Suite}; +use crate::internal_config::{sort_suites_by_id, Environment, GlobalConfig, Suite}; use crate::logging::log_and_return_error; use crate::sessions::session::{CurrentSession, RunOutcome, RunSpec, Session}; use robotmk::command_spec::CommandSpec; diff --git a/v2/robotmk/src/results.rs b/v2/robotmk/src/results.rs index 326d4790..f49c19f7 100644 --- a/v2/robotmk/src/results.rs +++ b/v2/robotmk/src/results.rs @@ -44,7 +44,7 @@ impl WriteSection for BuildStates<'_> { } } -#[derive(Serialize, Clone)] +#[derive(PartialEq, Debug, Serialize, Clone)] pub enum BuildOutcome { NotNeeded, Success(i64),