diff --git a/v2/robotmk/src/bin/scheduler/internal_config.rs b/v2/robotmk/src/bin/scheduler/internal_config.rs index da35d96f..5dd83bfa 100644 --- a/v2/robotmk/src/bin/scheduler/internal_config.rs +++ b/v2/robotmk/src/bin/scheduler/internal_config.rs @@ -98,7 +98,7 @@ mod tests { use crate::environment::{RCCEnvironment, SystemEnvironment}; use crate::sessions::session::{CurrentSession, UserSession}; use robotmk::config::{ - EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, RetryStrategy, + EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, RCCProfileConfig, RetryStrategy, RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig, }; @@ -157,6 +157,10 @@ mod tests { results_directory: Utf8PathBuf::from("/results"), rcc_config: RCCConfig { binary_path: Utf8PathBuf::from("/bin/rcc"), + profile_config: Some(RCCProfileConfig { + name: "Robotmk".into(), + path: "/rcc_profile_robotmk.yaml".into(), + }), }, suites: HashMap::from([ (String::from("system"), system_suite_config()), @@ -172,6 +176,10 @@ mod tests { global_config.rcc_config, RCCConfig { binary_path: Utf8PathBuf::from("/bin/rcc"), + profile_config: Some(RCCProfileConfig { + name: "Robotmk".into(), + path: "/rcc_profile_robotmk.yaml".into(), + }), } ); assert_eq!(suites.len(), 2); diff --git a/v2/robotmk/src/bin/scheduler/results.rs b/v2/robotmk/src/bin/scheduler/results.rs index c6d1b198..7db2fbfb 100644 --- a/v2/robotmk/src/bin/scheduler/results.rs +++ b/v2/robotmk/src/bin/scheduler/results.rs @@ -28,6 +28,7 @@ impl WriteSection for SchedulerPhase { #[derive(Serialize)] pub struct RCCSetupFailures { pub telemetry_disabling: Vec, + pub profile_configuring: Vec, pub long_path_support: Vec, pub shared_holotree: Vec, pub holotree_init: Vec, diff --git a/v2/robotmk/src/bin/scheduler/setup/rcc.rs b/v2/robotmk/src/bin/scheduler/setup/rcc.rs index c89d9234..dc08ae2a 100644 --- a/v2/robotmk/src/bin/scheduler/setup/rcc.rs +++ b/v2/robotmk/src/bin/scheduler/setup/rcc.rs @@ -9,9 +9,10 @@ use crate::sessions::session::{CurrentSession, RunOutcome, RunSpec, Session}; use anyhow::{bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use log::{debug, error}; -use robotmk::section::WriteSection; +use robotmk::{config::RCCProfileConfig, section::WriteSection}; use std::collections::HashMap; use std::fs::{create_dir_all, remove_dir_all}; +use std::vec; pub fn setup(global_config: &GlobalConfig, suites: Vec) -> Result> { adjust_rcc_binary_permissions(&global_config.rcc_config.binary_path) @@ -19,6 +20,10 @@ pub fn setup(global_config: &GlobalConfig, suites: Vec) -> Result, Vec) = suites .into_iter() @@ -50,17 +55,26 @@ fn rcc_setup_working_directory(working_directory: &Utf8Path) -> Utf8PathBuf { working_directory.join("rcc_setup") } +fn adjust_rcc_profile_permissions(profile_path: &Utf8Path) -> Result<()> { + debug!("Granting group `Users` read access to {profile_path}"); + run_icacls_command(vec![profile_path.as_str(), "/grant", "Users:(R)"]).context(format!( + "Adjusting permissions of {profile_path} for group `Users` failed", + )) +} + fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec) -> Result> { let mut rcc_setup_failures = RCCSetupFailures { telemetry_disabling: vec![], + profile_configuring: vec![], long_path_support: vec![], shared_holotree: vec![], holotree_init: vec![], }; debug!("Disabling RCC telemetry"); - let (sucessful_suites, failed_suites) = disable_rcc_telemetry(global_config, rcc_suites) - .context("Disabling RCC telemetry failed")?; + let (mut sucessful_suites, mut failed_suites) = + disable_rcc_telemetry(global_config, rcc_suites) + .context("Disabling RCC telemetry failed")?; rcc_setup_failures.telemetry_disabling = failed_suites.into_iter().map(|suite| suite.id).collect(); if !rcc_setup_failures.telemetry_disabling.is_empty() { @@ -70,10 +84,24 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec) -> Result) -> Result) -> Result, +) -> Result<(Vec, Vec)> { + let (sucessful_suites_import, failed_suites_import) = run_command_spec_per_session( + global_config, + suites, + &CommandSpec { + executable: global_config.rcc_config.binary_path.to_string(), + arguments: vec![ + "configuration".into(), + "import".into(), + "--filename".into(), + rcc_profile_config.path.to_string(), + ], + }, + "profile_import", + )?; + let (sucessful_suites_switch, failed_suites_switch) = run_command_spec_per_session( + global_config, + sucessful_suites_import, + &CommandSpec { + executable: global_config.rcc_config.binary_path.to_string(), + arguments: vec![ + "configuration".into(), + "switch".into(), + "--profile".into(), + rcc_profile_config.name.to_string(), + ], + }, + "profile_switch", + )?; + let mut failed_suites = vec![]; + failed_suites.extend(failed_suites_import); + failed_suites.extend(failed_suites_switch); + Ok((sucessful_suites_switch, failed_suites)) +} + fn enable_long_path_support( global_config: &GlobalConfig, suites: Vec, diff --git a/v2/robotmk/src/config.rs b/v2/robotmk/src/config.rs index b4c75e5f..d18bc8d9 100644 --- a/v2/robotmk/src/config.rs +++ b/v2/robotmk/src/config.rs @@ -21,6 +21,13 @@ pub struct Config { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct RCCConfig { pub binary_path: Utf8PathBuf, + pub profile_config: Option, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct RCCProfileConfig { + pub name: String, + pub path: Utf8PathBuf, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] diff --git a/v2/robotmk/tests/test_scheduler.rs b/v2/robotmk/tests/test_scheduler.rs index 8bcb2317..824e882c 100644 --- a/v2/robotmk/tests/test_scheduler.rs +++ b/v2/robotmk/tests/test_scheduler.rs @@ -2,8 +2,8 @@ use anyhow::Result; use assert_cmd::cargo::cargo_bin; use camino::{Utf8Path, Utf8PathBuf}; use robotmk::config::{ - Config, EnvironmentConfig, ExecutionConfig, RCCConfig, RCCEnvironmentConfig, RetryStrategy, - RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig, + Config, EnvironmentConfig, ExecutionConfig, RCCConfig, RCCEnvironmentConfig, RCCProfileConfig, + RetryStrategy, RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig, WorkingDirectoryCleanupConfig, }; use robotmk::section::Host; @@ -26,7 +26,10 @@ async fn test_scheduler() -> Result<()> { &Utf8PathBuf::from(var("CARGO_MANIFEST_DIR")?) .join("tests") .join("minimal_suite"), - var("RCC_BINARY_PATH")?, + RCCConfig { + binary_path: var("RCC_BINARY_PATH")?.into(), + profile_config: Some(create_rcc_profile(&test_dir)?), + }, ¤t_user_name, ); @@ -39,18 +42,35 @@ async fn test_scheduler() -> Result<()> { Ok(()) } +fn create_rcc_profile(test_dir: &Utf8Path) -> Result { + let rcc_profile_path = test_dir.join("rcc_profile.yaml"); + write( + &rcc_profile_path, + "name: Robotmk +description: Robotmk RCC profile +settings: + meta: + name: Robotmk + description: Robotmk RCC profile + source: Robotmk +", + )?; + Ok(RCCProfileConfig { + name: "Robotmk".into(), + path: rcc_profile_path, + }) +} + fn create_config( test_dir: &Utf8Path, suite_dir: &Utf8Path, - rcc_binary_path: impl Into, + rcc_config: RCCConfig, user_name_headed: &str, ) -> Config { Config { working_directory: test_dir.join("working"), results_directory: test_dir.join("results"), - rcc_config: RCCConfig { - binary_path: rcc_binary_path.into(), - }, + rcc_config, suites: [ ( String::from("rcc_headless"), @@ -165,7 +185,7 @@ async fn assert_working_directory( working_directory: &Utf8Path, headed_user_name: &str, ) -> Result<()> { - assert_working_directory_permissions(&working_directory).await?; + assert_permissions(&working_directory, "BUILTIN\\Users:(OI)(CI)(F)").await?; assert!(working_directory.is_dir()); assert_eq!( directory_entries(working_directory, 1), @@ -184,6 +204,22 @@ async fn assert_working_directory( &format!("holotree_initialization_user_{headed_user_name}.stdout"), "long_path_support_enabling.stderr", "long_path_support_enabling.stdout", + "profile_import_current_user.stderr", + "profile_import_current_user.stdout", + &format!("profile_import_user_{headed_user_name}.bat"), + &format!("profile_import_user_{headed_user_name}.exit_code"), + &format!("profile_import_user_{headed_user_name}.pid"), + &format!("profile_import_user_{headed_user_name}.run_flag"), + &format!("profile_import_user_{headed_user_name}.stderr"), + &format!("profile_import_user_{headed_user_name}.stdout"), + "profile_switch_current_user.stderr", + "profile_switch_current_user.stdout", + &format!("profile_switch_user_{headed_user_name}.bat"), + &format!("profile_switch_user_{headed_user_name}.exit_code"), + &format!("profile_switch_user_{headed_user_name}.pid"), + &format!("profile_switch_user_{headed_user_name}.run_flag"), + &format!("profile_switch_user_{headed_user_name}.stderr"), + &format!("profile_switch_user_{headed_user_name}.stdout"), "shared_holotree_init.stderr", "shared_holotree_init.stdout", "telemetry_disabling_current_user.stderr", @@ -227,11 +263,10 @@ async fn assert_working_directory( Ok(()) } -async fn assert_working_directory_permissions(working_directory: &impl AsRef) -> Result<()> { +async fn assert_permissions(path: impl AsRef, permissions: &str) -> Result<()> { let mut icacls_command = Command::new("icacls.exe"); - icacls_command.arg(working_directory); - let stdout = String::from_utf8(icacls_command.output().await?.stdout)?; - assert!(stdout.contains("BUILTIN\\Users:(OI)(CI)(F)")); + icacls_command.arg(path); + assert!(String::from_utf8(icacls_command.output().await?.stdout)?.contains(permissions)); Ok(()) } @@ -252,21 +287,21 @@ fn assert_results_directory(results_directory: &Utf8Path) { } async fn assert_rcc(rcc_config: &RCCConfig) -> Result<()> { - assert_rcc_binary_permissions(&rcc_config.binary_path).await?; - assert_rcc_configuration(&rcc_config.binary_path).await?; + assert_rcc_files_permissions(rcc_config).await?; + assert_rcc_configuration(rcc_config).await?; assert_rcc_longpath_support_enabled(&rcc_config.binary_path).await } -async fn assert_rcc_binary_permissions(rcc_binary_path: impl AsRef) -> Result<()> { - let mut icacls_command = Command::new("icacls.exe"); - icacls_command.arg(rcc_binary_path); - let stdout = String::from_utf8(icacls_command.output().await?.stdout)?; - assert!(stdout.contains("BUILTIN\\Users:(RX)")); - Ok(()) +async fn assert_rcc_files_permissions(rcc_config: &RCCConfig) -> Result<()> { + assert_permissions(&rcc_config.binary_path, "BUILTIN\\Users:(RX)").await?; + let Some(rcc_profile_config) = &rcc_config.profile_config else { + return Ok(()); + }; + assert_permissions(&rcc_profile_config.path, "BUILTIN\\Users:(R)").await } -async fn assert_rcc_configuration(rcc_binary_path: impl AsRef) -> Result<()> { - let mut rcc_config_diag_command = Command::new(rcc_binary_path); +async fn assert_rcc_configuration(rcc_config: &RCCConfig) -> Result<()> { + let mut rcc_config_diag_command = Command::new(&rcc_config.binary_path); rcc_config_diag_command .arg("configuration") .arg("diagnostics"); @@ -274,6 +309,12 @@ async fn assert_rcc_configuration(rcc_binary_path: impl AsRef) -> Result< assert!(stdout.contains("telemetry-enabled ... \"false\"")); assert!(stdout.contains("holotree-shared ... \"true\"")); assert!(stdout.contains("holotree-global-shared ... \"true\"")); + if let Some(rcc_profile_config) = &rcc_config.profile_config { + assert!(stdout.contains(&format!( + "config-active-profile ... \"{}\"", + rcc_profile_config.name + ))); + } Ok(()) }