Skip to content

Commit

Permalink
Implement optional RCC profile path as part of scheduler configuration
Browse files Browse the repository at this point in the history
If set, the profile will be set during the RCC setup.

CMK-15162
  • Loading branch information
jherbel committed Dec 4, 2023
1 parent 87d95ce commit 182cc21
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 31 deletions.
10 changes: 9 additions & 1 deletion v2/robotmk/src/bin/scheduler/internal_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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()),
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions v2/robotmk/src/bin/scheduler/results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl WriteSection for SchedulerPhase {
#[derive(Serialize)]
pub struct RCCSetupFailures {
pub telemetry_disabling: Vec<String>,
pub profile_configuring: Vec<String>,
pub long_path_support: Vec<String>,
pub shared_holotree: Vec<String>,
pub holotree_init: Vec<String>,
Expand Down
83 changes: 75 additions & 8 deletions v2/robotmk/src/bin/scheduler/setup/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ 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<Suite>) -> Result<Vec<Suite>> {
adjust_rcc_binary_permissions(&global_config.rcc_config.binary_path)
.context("Failed to adjust permissions of RCC binary")?;
clear_rcc_setup_working_directory(&rcc_setup_working_directory(
&global_config.working_directory,
))?;
if let Some(rcc_profile_config) = &global_config.rcc_config.profile_config {
adjust_rcc_profile_permissions(&rcc_profile_config.path)
.context("Failed to adjust permissions of RCC profile")?;
}

let (rcc_suites, mut surviving_suites): (Vec<Suite>, Vec<Suite>) = suites
.into_iter()
Expand Down Expand Up @@ -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<Suite>) -> Result<Vec<Suite>> {
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() {
Expand All @@ -70,10 +84,24 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec
);
}

if let Some(rcc_profile_config) = &global_config.rcc_config.profile_config {
debug!("Configuring RCC profile");
(sucessful_suites, failed_suites) =
configure_rcc_profile(rcc_profile_config, global_config, sucessful_suites)
.context("Configuring RCC profile failed")?;
rcc_setup_failures.profile_configuring =
failed_suites.into_iter().map(|suite| suite.id).collect();
if !rcc_setup_failures.profile_configuring.is_empty() {
error!(
"Dropping the following suites due to profile configuring failure: {}",
rcc_setup_failures.profile_configuring.join(", ")
);
}
}

debug!("Enabling support for long paths");
let (sucessful_suites, failed_suites) =
enable_long_path_support(global_config, sucessful_suites)
.context("Enabling support for long paths failed")?;
(sucessful_suites, failed_suites) = enable_long_path_support(global_config, sucessful_suites)
.context("Enabling support for long paths failed")?;
rcc_setup_failures.long_path_support =
failed_suites.into_iter().map(|suite| suite.id).collect();
if !rcc_setup_failures.long_path_support.is_empty() {
Expand All @@ -84,7 +112,7 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec
}

debug!("Initializing shared holotree");
let (sucessful_suites, failed_suites) = shared_holotree_init(global_config, sucessful_suites)
(sucessful_suites, failed_suites) = shared_holotree_init(global_config, sucessful_suites)
.context("Shared holotree initialization failed")?;
rcc_setup_failures.shared_holotree = failed_suites.into_iter().map(|suite| suite.id).collect();
if !rcc_setup_failures.shared_holotree.is_empty() {
Expand All @@ -95,7 +123,7 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec
}

debug!("Initializing holotree");
let (sucessful_suites, failed_suites) =
(sucessful_suites, failed_suites) =
holotree_init(global_config, sucessful_suites).context("Holotree initialization failed")?;
rcc_setup_failures.holotree_init = failed_suites.into_iter().map(|suite| suite.id).collect();
if !rcc_setup_failures.holotree_init.is_empty() {
Expand Down Expand Up @@ -132,6 +160,45 @@ fn disable_rcc_telemetry(
)
}

fn configure_rcc_profile(
rcc_profile_config: &RCCProfileConfig,
global_config: &GlobalConfig,
suites: Vec<Suite>,
) -> Result<(Vec<Suite>, Vec<Suite>)> {
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<Suite>,
Expand Down
7 changes: 7 additions & 0 deletions v2/robotmk/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub struct Config {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RCCConfig {
pub binary_path: Utf8PathBuf,
pub profile_config: Option<RCCProfileConfig>,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RCCProfileConfig {
pub name: String,
pub path: Utf8PathBuf,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
Expand Down
85 changes: 63 additions & 22 deletions v2/robotmk/tests/test_scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)?),
},
&current_user_name,
);

Expand All @@ -39,18 +42,35 @@ async fn test_scheduler() -> Result<()> {
Ok(())
}

fn create_rcc_profile(test_dir: &Utf8Path) -> Result<RCCProfileConfig> {
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<Utf8PathBuf>,
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"),
Expand Down Expand Up @@ -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),
Expand All @@ -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",
Expand Down Expand Up @@ -227,11 +263,10 @@ async fn assert_working_directory(
Ok(())
}

async fn assert_working_directory_permissions(working_directory: &impl AsRef<OsStr>) -> Result<()> {
async fn assert_permissions(path: impl AsRef<OsStr>, 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(())
}

Expand All @@ -252,28 +287,34 @@ 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<OsStr>) -> 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<OsStr>) -> 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");
let stdout = String::from_utf8(rcc_config_diag_command.output().await?.stdout)?;
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(())
}

Expand Down

0 comments on commit 182cc21

Please sign in to comment.