diff --git a/src/bin/scheduler/internal_config.rs b/src/bin/scheduler/internal_config.rs index dac9c67b..963ec4c2 100644 --- a/src/bin/scheduler/internal_config.rs +++ b/src/bin/scheduler/internal_config.rs @@ -46,44 +46,44 @@ pub fn from_external_config( cancellation_token: CancellationToken, results_directory_locker: Locker, ) -> (GlobalConfig, Vec) { - let mut suites: Vec = external_config - .suites - .into_iter() - .enumerate() - .map(|(suite_index, (suite_id, suite_config))| Suite { - id: suite_id.clone(), - working_directory: external_config - .working_directory - .join("suites") - .join(&suite_id), - results_file: suite_results_directory(&external_config.results_directory) - .join(format!("{}.json", suite_id)), - timeout: suite_config.execution_config.timeout, - robot: Robot { - robot_target: suite_config.robot_config.robot_target, - command_line_args: suite_config.robot_config.command_line_args, - n_attempts_max: suite_config.execution_config.n_attempts_max, - retry_strategy: suite_config.execution_config.retry_strategy, - }, - environment: Environment::new( - &suite_id, - &external_config.rcc_config.binary_path, - &suite_config.environment_config, - ), - session: Session::new(&suite_config.session_config), - working_directory_cleanup_config: suite_config.working_directory_cleanup_config, - cancellation_token: cancellation_token.clone(), - host: suite_config.host, - results_directory_locker: results_directory_locker.clone(), - metadata: suite_config.metadata, - group_affiliation: GroupAffiliation { - group_index: suite_index, - position_in_group: 0, - execution_interval: suite_config.execution_config.execution_interval_seconds, - }, - }) - .collect(); - sort_suites_by_id(&mut suites); + let mut suites = vec![]; + + for (group_index, sequential_group) in external_config.suite_groups.into_iter().enumerate() { + for (suite_index, suite_config) in sequential_group.suites.into_iter().enumerate() { + suites.push(Suite { + id: suite_config.id.clone(), + working_directory: external_config + .working_directory + .join("suites") + .join(&suite_config.id), + results_file: suite_results_directory(&external_config.results_directory) + .join(format!("{}.json", suite_config.id)), + timeout: suite_config.execution_config.timeout, + robot: Robot { + robot_target: suite_config.robot_config.robot_target, + command_line_args: suite_config.robot_config.command_line_args, + n_attempts_max: suite_config.execution_config.n_attempts_max, + retry_strategy: suite_config.execution_config.retry_strategy, + }, + environment: Environment::new( + &suite_config.id, + &external_config.rcc_config.binary_path, + &suite_config.environment_config, + ), + session: Session::new(&suite_config.session_config), + working_directory_cleanup_config: suite_config.working_directory_cleanup_config, + cancellation_token: cancellation_token.clone(), + host: suite_config.host, + results_directory_locker: results_directory_locker.clone(), + metadata: suite_config.metadata, + group_affiliation: GroupAffiliation { + group_index, + position_in_group: suite_index, + execution_interval: sequential_group.execution_interval, + }, + }); + } + } ( GlobalConfig { working_directory: external_config.working_directory, @@ -96,8 +96,13 @@ pub fn from_external_config( ) } -pub fn sort_suites_by_id(suites: &mut [Suite]) { - suites.sort_by_key(|suite| suite.id.to_string()); +pub fn sort_suites_by_grouping(suites: &mut [Suite]) { + suites.sort_by_key(|suite| { + ( + suite.group_affiliation.group_index, + suite.group_affiliation.position_in_group, + ) + }); } #[cfg(test)] @@ -105,14 +110,14 @@ mod tests { use super::*; use robotmk::config::{ CustomRCCProfileConfig, EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, - RCCProfileConfig, RetryStrategy, RobotConfig, SessionConfig, SuiteConfig, - UserSessionConfig, + RCCProfileConfig, RetryStrategy, RobotConfig, SequentialSuiteGroup, SessionConfig, + SuiteConfig, UserSessionConfig, }; use robotmk::environment::{Environment, RCCEnvironment, SystemEnvironment}; - use std::collections::HashMap; fn system_suite_config() -> SuiteConfig { SuiteConfig { + id: "system".into(), robot_config: RobotConfig { robot_target: Utf8PathBuf::from("/suite/system/tasks.robot"), command_line_args: vec![], @@ -120,7 +125,6 @@ mod tests { execution_config: ExecutionConfig { n_attempts_max: 1, retry_strategy: RetryStrategy::Incremental, - execution_interval_seconds: 300, timeout: 60, }, environment_config: EnvironmentConfig::System, @@ -135,6 +139,7 @@ mod tests { fn rcc_suite_config() -> SuiteConfig { SuiteConfig { + id: "rcc".into(), robot_config: RobotConfig { robot_target: Utf8PathBuf::from("/suite/rcc/tasks.robot"), command_line_args: vec![], @@ -142,7 +147,6 @@ mod tests { execution_config: ExecutionConfig { n_attempts_max: 1, retry_strategy: RetryStrategy::Complete, - execution_interval_seconds: 300, timeout: 60, }, environment_config: EnvironmentConfig::Rcc(RCCEnvironmentConfig { @@ -175,10 +179,16 @@ mod tests { path: "/rcc_profile_robotmk.yaml".into(), }), }, - suites: HashMap::from([ - (String::from("system"), system_suite_config()), - (String::from("rcc"), rcc_suite_config()), - ]), + suite_groups: vec![ + SequentialSuiteGroup { + suites: vec![rcc_suite_config()], + execution_interval: 300, + }, + SequentialSuiteGroup { + suites: vec![system_suite_config()], + execution_interval: 300, + }, + ], }, cancellation_token.clone(), Locker::new("/config.json", Some(&cancellation_token)), @@ -233,7 +243,7 @@ mod tests { assert_eq!( suites[0].group_affiliation, GroupAffiliation { - group_index: 1, + group_index: 0, position_in_group: 0, execution_interval: 300, } @@ -268,7 +278,7 @@ mod tests { assert_eq!( suites[1].group_affiliation, GroupAffiliation { - group_index: 0, + group_index: 1, position_in_group: 0, execution_interval: 300, } diff --git a/src/bin/scheduler/setup/rcc.rs b/src/bin/scheduler/setup/rcc.rs index bbf9c841..598b41e4 100644 --- a/src/bin/scheduler/setup/rcc.rs +++ b/src/bin/scheduler/setup/rcc.rs @@ -1,6 +1,6 @@ use super::all_configured_users; use super::icacls::run_icacls_command; -use crate::internal_config::{sort_suites_by_id, GlobalConfig, Suite}; +use crate::internal_config::{sort_suites_by_grouping, GlobalConfig, Suite}; use crate::logging::log_and_return_error; use robotmk::command_spec::CommandSpec; use robotmk::environment::Environment; @@ -42,7 +42,7 @@ pub fn setup(global_config: &GlobalConfig, suites: Vec) -> AnyhowResult AnyhowResult { @@ -15,7 +14,7 @@ pub struct Config { pub working_directory: Utf8PathBuf, pub results_directory: Utf8PathBuf, pub rcc_config: RCCConfig, - pub suites: HashMap, + pub suite_groups: Vec, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -36,8 +35,15 @@ pub struct CustomRCCProfileConfig { pub path: Utf8PathBuf, } +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct SequentialSuiteGroup { + pub suites: Vec, + pub execution_interval: u64, +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SuiteConfig { + pub id: String, pub robot_config: RobotConfig, pub execution_config: ExecutionConfig, pub environment_config: EnvironmentConfig, @@ -57,7 +63,6 @@ pub struct RobotConfig { pub struct ExecutionConfig { pub n_attempts_max: usize, pub retry_strategy: RetryStrategy, - pub execution_interval_seconds: u64, pub timeout: u64, } diff --git a/tests/test_scheduler.rs b/tests/test_scheduler.rs index 85d36a20..b5becb4d 100644 --- a/tests/test_scheduler.rs +++ b/tests/test_scheduler.rs @@ -3,8 +3,8 @@ use assert_cmd::cargo::cargo_bin; use camino::{Utf8Path, Utf8PathBuf}; use robotmk::config::{ Config, CustomRCCProfileConfig, EnvironmentConfig, ExecutionConfig, RCCConfig, - RCCEnvironmentConfig, RCCProfileConfig, RetryStrategy, RobotConfig, SessionConfig, SuiteConfig, - SuiteMetadata, UserSessionConfig, WorkingDirectoryCleanupConfig, + RCCEnvironmentConfig, RCCProfileConfig, RetryStrategy, RobotConfig, SequentialSuiteGroup, + SessionConfig, SuiteConfig, SuiteMetadata, UserSessionConfig, WorkingDirectoryCleanupConfig, }; use robotmk::section::Host; use serde_json::to_string; @@ -73,71 +73,69 @@ fn create_config( working_directory: test_dir.join("working"), results_directory: test_dir.join("results"), rcc_config, - suites: [ - ( - String::from("rcc_headless"), - SuiteConfig { - robot_config: RobotConfig { - robot_target: suite_dir.join("tasks.robot"), - command_line_args: vec![], - }, - execution_config: ExecutionConfig { - n_attempts_max: 1, - retry_strategy: RetryStrategy::Complete, - execution_interval_seconds: 30, - timeout: 10, - }, - environment_config: EnvironmentConfig::Rcc(RCCEnvironmentConfig { - robot_yaml_path: suite_dir.join("robot.yaml"), - build_timeout: 1200, - env_json_path: None, - }), - session_config: SessionConfig::Current, - working_directory_cleanup_config: WorkingDirectoryCleanupConfig::MaxExecutions( - 4, - ), - host: Host::Source, - metadata: SuiteMetadata { - application: "app1".into(), - }, - }, - ), - ( - String::from("rcc_headed"), - SuiteConfig { - robot_config: RobotConfig { - robot_target: suite_dir.join("tasks.robot"), - command_line_args: vec![], - }, - execution_config: ExecutionConfig { - n_attempts_max: 1, - retry_strategy: RetryStrategy::Complete, - execution_interval_seconds: 45, - timeout: 15, + suite_groups: vec![ + SequentialSuiteGroup { + suites: vec![ + SuiteConfig { + id: "rcc_headless".into(), + robot_config: RobotConfig { + robot_target: suite_dir.join("tasks.robot"), + command_line_args: vec![], + }, + execution_config: ExecutionConfig { + n_attempts_max: 1, + retry_strategy: RetryStrategy::Complete, + timeout: 10, + }, + environment_config: EnvironmentConfig::Rcc(RCCEnvironmentConfig { + robot_yaml_path: suite_dir.join("robot.yaml"), + build_timeout: 1200, + env_json_path: None, + }), + session_config: SessionConfig::Current, + working_directory_cleanup_config: + WorkingDirectoryCleanupConfig::MaxExecutions(4), + host: Host::Source, + metadata: SuiteMetadata { + application: "app1".into(), + }, }, - environment_config: EnvironmentConfig::Rcc(RCCEnvironmentConfig { - robot_yaml_path: suite_dir.join("robot.yaml"), - build_timeout: 1200, - env_json_path: None, - }), - session_config: SessionConfig::SpecificUser(UserSessionConfig { - user_name: user_name_headed.into(), - }), - working_directory_cleanup_config: WorkingDirectoryCleanupConfig::MaxAgeSecs( - 120, - ), - host: Host::Source, - metadata: SuiteMetadata { - application: "app2".into(), + SuiteConfig { + id: "rcc_headed".into(), + robot_config: RobotConfig { + robot_target: suite_dir.join("tasks.robot"), + command_line_args: vec![], + }, + execution_config: ExecutionConfig { + n_attempts_max: 1, + retry_strategy: RetryStrategy::Complete, + timeout: 15, + }, + environment_config: EnvironmentConfig::Rcc(RCCEnvironmentConfig { + robot_yaml_path: suite_dir.join("robot.yaml"), + build_timeout: 1200, + env_json_path: None, + }), + session_config: SessionConfig::SpecificUser(UserSessionConfig { + user_name: user_name_headed.into(), + }), + working_directory_cleanup_config: WorkingDirectoryCleanupConfig::MaxAgeSecs( + 120, + ), + host: Host::Source, + metadata: SuiteMetadata { + application: "app2".into(), + }, }, - }, - ), + ], + execution_interval: 30, + }, // Note: For our test, it doesn't matter if the suite can be executed on the target // system. We are not checking for success. So even on systems with no Python, the test // will succeed. - ( - String::from("no_rcc"), - SuiteConfig { + SequentialSuiteGroup { + suites: vec![SuiteConfig { + id: "no_rcc".into(), robot_config: RobotConfig { robot_target: suite_dir.join("tasks.robot"), command_line_args: vec![], @@ -145,7 +143,6 @@ fn create_config( execution_config: ExecutionConfig { n_attempts_max: 1, retry_strategy: RetryStrategy::Complete, - execution_interval_seconds: 37, timeout: 17, }, environment_config: EnvironmentConfig::System, @@ -157,10 +154,10 @@ fn create_config( metadata: SuiteMetadata { application: "app3".into(), }, - }, - ), - ] - .into(), + }], + execution_interval: 37, + }, + ], } }