Skip to content

Commit 0129df7

Browse files
committed
Implement optional RCC profile path as part of scheduler configuration
If set, the profile will be set during the RCC setup. CMK-15162
1 parent 543be92 commit 0129df7

File tree

5 files changed

+155
-31
lines changed

5 files changed

+155
-31
lines changed

v2/robotmk/src/bin/scheduler/internal_config.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ mod tests {
9898
use crate::environment::{RCCEnvironment, SystemEnvironment};
9999
use crate::sessions::session::{CurrentSession, UserSession};
100100
use robotmk::config::{
101-
EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, RetryStrategy,
101+
EnvironmentConfig, ExecutionConfig, RCCEnvironmentConfig, RCCProfileConfig, RetryStrategy,
102102
RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig,
103103
};
104104

@@ -157,6 +157,10 @@ mod tests {
157157
results_directory: Utf8PathBuf::from("/results"),
158158
rcc_config: RCCConfig {
159159
binary_path: Utf8PathBuf::from("/bin/rcc"),
160+
profile_config: Some(RCCProfileConfig {
161+
name: "Robotmk".into(),
162+
path: "/rcc_profile_robotmk.yaml".into(),
163+
}),
160164
},
161165
suites: HashMap::from([
162166
(String::from("system"), system_suite_config()),
@@ -172,6 +176,10 @@ mod tests {
172176
global_config.rcc_config,
173177
RCCConfig {
174178
binary_path: Utf8PathBuf::from("/bin/rcc"),
179+
profile_config: Some(RCCProfileConfig {
180+
name: "Robotmk".into(),
181+
path: "/rcc_profile_robotmk.yaml".into(),
182+
}),
175183
}
176184
);
177185
assert_eq!(suites.len(), 2);

v2/robotmk/src/bin/scheduler/results.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ impl WriteSection for SchedulerPhase {
2828
#[derive(Serialize)]
2929
pub struct RCCSetupFailures {
3030
pub telemetry_disabling: Vec<String>,
31+
pub profile_configuring: Vec<String>,
3132
pub long_path_support: Vec<String>,
3233
pub shared_holotree: Vec<String>,
3334
pub holotree_init: Vec<String>,

v2/robotmk/src/bin/scheduler/setup/rcc.rs

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ use crate::sessions::session::{CurrentSession, RunOutcome, RunSpec, Session};
99
use anyhow::{bail, Context, Result};
1010
use camino::{Utf8Path, Utf8PathBuf};
1111
use log::{debug, error};
12-
use robotmk::section::WriteSection;
12+
use robotmk::{config::RCCProfileConfig, section::WriteSection};
1313
use std::collections::HashMap;
1414
use std::fs::{create_dir_all, remove_dir_all};
15+
use std::vec;
1516

1617
pub fn setup(global_config: &GlobalConfig, suites: Vec<Suite>) -> Result<Vec<Suite>> {
1718
adjust_rcc_binary_permissions(&global_config.rcc_config.binary_path)
1819
.context("Failed to adjust permissions of RCC binary")?;
1920
clear_rcc_setup_working_directory(&rcc_setup_working_directory(
2021
&global_config.working_directory,
2122
))?;
23+
if let Some(rcc_profile_config) = &global_config.rcc_config.profile_config {
24+
adjust_rcc_profile_permissions(&rcc_profile_config.path)
25+
.context("Failed to adjust permissions of RCC profile")?;
26+
}
2227

2328
let (rcc_suites, mut surviving_suites): (Vec<Suite>, Vec<Suite>) = suites
2429
.into_iter()
@@ -50,17 +55,26 @@ fn rcc_setup_working_directory(working_directory: &Utf8Path) -> Utf8PathBuf {
5055
working_directory.join("rcc_setup")
5156
}
5257

58+
fn adjust_rcc_profile_permissions(profile_path: &Utf8Path) -> Result<()> {
59+
debug!("Granting group `Users` read access to {profile_path}");
60+
run_icacls_command(vec![profile_path.as_str(), "/grant", "Users:(R)"]).context(format!(
61+
"Adjusting permissions of {profile_path} for group `Users` failed",
62+
))
63+
}
64+
5365
fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec<Suite>> {
5466
let mut rcc_setup_failures = RCCSetupFailures {
5567
telemetry_disabling: vec![],
68+
profile_configuring: vec![],
5669
long_path_support: vec![],
5770
shared_holotree: vec![],
5871
holotree_init: vec![],
5972
};
6073

6174
debug!("Disabling RCC telemetry");
62-
let (sucessful_suites, failed_suites) = disable_rcc_telemetry(global_config, rcc_suites)
63-
.context("Disabling RCC telemetry failed")?;
75+
let (mut sucessful_suites, mut failed_suites) =
76+
disable_rcc_telemetry(global_config, rcc_suites)
77+
.context("Disabling RCC telemetry failed")?;
6478
rcc_setup_failures.telemetry_disabling =
6579
failed_suites.into_iter().map(|suite| suite.id).collect();
6680
if !rcc_setup_failures.telemetry_disabling.is_empty() {
@@ -70,10 +84,24 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec
7084
);
7185
}
7286

87+
if let Some(rcc_profile_config) = &global_config.rcc_config.profile_config {
88+
debug!("Configuring RCC profile");
89+
(sucessful_suites, failed_suites) =
90+
configure_rcc_profile(rcc_profile_config, global_config, sucessful_suites)
91+
.context("Configuring RCC profile failed")?;
92+
rcc_setup_failures.profile_configuring =
93+
failed_suites.into_iter().map(|suite| suite.id).collect();
94+
if !rcc_setup_failures.profile_configuring.is_empty() {
95+
error!(
96+
"Dropping the following suites due to profile configuring failure: {}",
97+
rcc_setup_failures.profile_configuring.join(", ")
98+
);
99+
}
100+
}
101+
73102
debug!("Enabling support for long paths");
74-
let (sucessful_suites, failed_suites) =
75-
enable_long_path_support(global_config, sucessful_suites)
76-
.context("Enabling support for long paths failed")?;
103+
(sucessful_suites, failed_suites) = enable_long_path_support(global_config, sucessful_suites)
104+
.context("Enabling support for long paths failed")?;
77105
rcc_setup_failures.long_path_support =
78106
failed_suites.into_iter().map(|suite| suite.id).collect();
79107
if !rcc_setup_failures.long_path_support.is_empty() {
@@ -84,7 +112,7 @@ fn rcc_setup(global_config: &GlobalConfig, rcc_suites: Vec<Suite>) -> Result<Vec
84112
}
85113

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

97125
debug!("Initializing holotree");
98-
let (sucessful_suites, failed_suites) =
126+
(sucessful_suites, failed_suites) =
99127
holotree_init(global_config, sucessful_suites).context("Holotree initialization failed")?;
100128
rcc_setup_failures.holotree_init = failed_suites.into_iter().map(|suite| suite.id).collect();
101129
if !rcc_setup_failures.holotree_init.is_empty() {
@@ -132,6 +160,45 @@ fn disable_rcc_telemetry(
132160
)
133161
}
134162

163+
fn configure_rcc_profile(
164+
rcc_profile_config: &RCCProfileConfig,
165+
global_config: &GlobalConfig,
166+
suites: Vec<Suite>,
167+
) -> Result<(Vec<Suite>, Vec<Suite>)> {
168+
let (sucessful_suites_import, failed_suites_import) = run_command_spec_per_session(
169+
global_config,
170+
suites,
171+
&CommandSpec {
172+
executable: global_config.rcc_config.binary_path.to_string(),
173+
arguments: vec![
174+
"configuration".into(),
175+
"import".into(),
176+
"--filename".into(),
177+
rcc_profile_config.path.to_string(),
178+
],
179+
},
180+
"profile_import",
181+
)?;
182+
let (sucessful_suites_switch, failed_suites_switch) = run_command_spec_per_session(
183+
global_config,
184+
sucessful_suites_import,
185+
&CommandSpec {
186+
executable: global_config.rcc_config.binary_path.to_string(),
187+
arguments: vec![
188+
"configuration".into(),
189+
"switch".into(),
190+
"--profile".into(),
191+
rcc_profile_config.name.to_string(),
192+
],
193+
},
194+
"profile_switch",
195+
)?;
196+
let mut failed_suites = vec![];
197+
failed_suites.extend(failed_suites_import);
198+
failed_suites.extend(failed_suites_switch);
199+
Ok((sucessful_suites_switch, failed_suites))
200+
}
201+
135202
fn enable_long_path_support(
136203
global_config: &GlobalConfig,
137204
suites: Vec<Suite>,

v2/robotmk/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ pub struct Config {
2121
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
2222
pub struct RCCConfig {
2323
pub binary_path: Utf8PathBuf,
24+
pub profile_config: Option<RCCProfileConfig>,
25+
}
26+
27+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
28+
pub struct RCCProfileConfig {
29+
pub name: String,
30+
pub path: Utf8PathBuf,
2431
}
2532

2633
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]

v2/robotmk/tests/test_scheduler.rs

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use anyhow::Result;
22
use assert_cmd::cargo::cargo_bin;
33
use camino::{Utf8Path, Utf8PathBuf};
44
use robotmk::config::{
5-
Config, EnvironmentConfig, ExecutionConfig, RCCConfig, RCCEnvironmentConfig, RetryStrategy,
6-
RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig,
5+
Config, EnvironmentConfig, ExecutionConfig, RCCConfig, RCCEnvironmentConfig, RCCProfileConfig,
6+
RetryStrategy, RobotFrameworkConfig, SessionConfig, SuiteConfig, UserSessionConfig,
77
WorkingDirectoryCleanupConfig,
88
};
99
use robotmk::section::Host;
@@ -26,7 +26,10 @@ async fn test_scheduler() -> Result<()> {
2626
&Utf8PathBuf::from(var("CARGO_MANIFEST_DIR")?)
2727
.join("tests")
2828
.join("minimal_suite"),
29-
var("RCC_BINARY_PATH")?,
29+
RCCConfig {
30+
binary_path: var("RCC_BINARY_PATH")?.into(),
31+
profile_config: Some(create_rcc_profile(&test_dir)?),
32+
},
3033
&current_user_name,
3134
);
3235

@@ -39,18 +42,35 @@ async fn test_scheduler() -> Result<()> {
3942
Ok(())
4043
}
4144

45+
fn create_rcc_profile(test_dir: &Utf8Path) -> Result<RCCProfileConfig> {
46+
let rcc_profile_path = test_dir.join("rcc_profile.yaml");
47+
write(
48+
&rcc_profile_path,
49+
"name: Robotmk
50+
description: Robotmk RCC profile
51+
settings:
52+
meta:
53+
name: Robotmk
54+
description: Robotmk RCC profile
55+
source: Robotmk
56+
",
57+
)?;
58+
Ok(RCCProfileConfig {
59+
name: "Robotmk".into(),
60+
path: rcc_profile_path,
61+
})
62+
}
63+
4264
fn create_config(
4365
test_dir: &Utf8Path,
4466
suite_dir: &Utf8Path,
45-
rcc_binary_path: impl Into<Utf8PathBuf>,
67+
rcc_config: RCCConfig,
4668
user_name_headed: &str,
4769
) -> Config {
4870
Config {
4971
working_directory: test_dir.join("working"),
5072
results_directory: test_dir.join("results"),
51-
rcc_config: RCCConfig {
52-
binary_path: rcc_binary_path.into(),
53-
},
73+
rcc_config,
5474
suites: [
5575
(
5676
String::from("rcc_headless"),
@@ -165,7 +185,7 @@ async fn assert_working_directory(
165185
working_directory: &Utf8Path,
166186
headed_user_name: &str,
167187
) -> Result<()> {
168-
assert_working_directory_permissions(&working_directory).await?;
188+
assert_permissions(&working_directory, "BUILTIN\\Users:(OI)(CI)(F)").await?;
169189
assert!(working_directory.is_dir());
170190
assert_eq!(
171191
directory_entries(working_directory, 1),
@@ -184,6 +204,22 @@ async fn assert_working_directory(
184204
&format!("holotree_initialization_user_{headed_user_name}.stdout"),
185205
"long_path_support_enabling.stderr",
186206
"long_path_support_enabling.stdout",
207+
"profile_import_current_user.stderr",
208+
"profile_import_current_user.stdout",
209+
&format!("profile_import_user_{headed_user_name}.bat"),
210+
&format!("profile_import_user_{headed_user_name}.exit_code"),
211+
&format!("profile_import_user_{headed_user_name}.pid"),
212+
&format!("profile_import_user_{headed_user_name}.run_flag"),
213+
&format!("profile_import_user_{headed_user_name}.stderr"),
214+
&format!("profile_import_user_{headed_user_name}.stdout"),
215+
"profile_switch_current_user.stderr",
216+
"profile_switch_current_user.stdout",
217+
&format!("profile_switch_user_{headed_user_name}.bat"),
218+
&format!("profile_switch_user_{headed_user_name}.exit_code"),
219+
&format!("profile_switch_user_{headed_user_name}.pid"),
220+
&format!("profile_switch_user_{headed_user_name}.run_flag"),
221+
&format!("profile_switch_user_{headed_user_name}.stderr"),
222+
&format!("profile_switch_user_{headed_user_name}.stdout"),
187223
"shared_holotree_init.stderr",
188224
"shared_holotree_init.stdout",
189225
"telemetry_disabling_current_user.stderr",
@@ -227,11 +263,10 @@ async fn assert_working_directory(
227263
Ok(())
228264
}
229265

230-
async fn assert_working_directory_permissions(working_directory: &impl AsRef<OsStr>) -> Result<()> {
266+
async fn assert_permissions(path: impl AsRef<OsStr>, permissions: &str) -> Result<()> {
231267
let mut icacls_command = Command::new("icacls.exe");
232-
icacls_command.arg(working_directory);
233-
let stdout = String::from_utf8(icacls_command.output().await?.stdout)?;
234-
assert!(stdout.contains("BUILTIN\\Users:(OI)(CI)(F)"));
268+
icacls_command.arg(path);
269+
assert!(String::from_utf8(icacls_command.output().await?.stdout)?.contains(permissions));
235270
Ok(())
236271
}
237272

@@ -252,28 +287,34 @@ fn assert_results_directory(results_directory: &Utf8Path) {
252287
}
253288

254289
async fn assert_rcc(rcc_config: &RCCConfig) -> Result<()> {
255-
assert_rcc_binary_permissions(&rcc_config.binary_path).await?;
256-
assert_rcc_configuration(&rcc_config.binary_path).await?;
290+
assert_rcc_files_permissions(rcc_config).await?;
291+
assert_rcc_configuration(rcc_config).await?;
257292
assert_rcc_longpath_support_enabled(&rcc_config.binary_path).await
258293
}
259294

260-
async fn assert_rcc_binary_permissions(rcc_binary_path: impl AsRef<OsStr>) -> Result<()> {
261-
let mut icacls_command = Command::new("icacls.exe");
262-
icacls_command.arg(rcc_binary_path);
263-
let stdout = String::from_utf8(icacls_command.output().await?.stdout)?;
264-
assert!(stdout.contains("BUILTIN\\Users:(RX)"));
265-
Ok(())
295+
async fn assert_rcc_files_permissions(rcc_config: &RCCConfig) -> Result<()> {
296+
assert_permissions(&rcc_config.binary_path, "BUILTIN\\Users:(RX)").await?;
297+
let Some(rcc_profile_config) = &rcc_config.profile_config else {
298+
return Ok(());
299+
};
300+
assert_permissions(&rcc_profile_config.path, "BUILTIN\\Users:(R)").await
266301
}
267302

268-
async fn assert_rcc_configuration(rcc_binary_path: impl AsRef<OsStr>) -> Result<()> {
269-
let mut rcc_config_diag_command = Command::new(rcc_binary_path);
303+
async fn assert_rcc_configuration(rcc_config: &RCCConfig) -> Result<()> {
304+
let mut rcc_config_diag_command = Command::new(&rcc_config.binary_path);
270305
rcc_config_diag_command
271306
.arg("configuration")
272307
.arg("diagnostics");
273308
let stdout = String::from_utf8(rcc_config_diag_command.output().await?.stdout)?;
274309
assert!(stdout.contains("telemetry-enabled ... \"false\""));
275310
assert!(stdout.contains("holotree-shared ... \"true\""));
276311
assert!(stdout.contains("holotree-global-shared ... \"true\""));
312+
if let Some(rcc_profile_config) = &rcc_config.profile_config {
313+
assert!(stdout.contains(&format!(
314+
"config-active-profile ... \"{}\"",
315+
rcc_profile_config.name
316+
)));
317+
}
277318
Ok(())
278319
}
279320

0 commit comments

Comments
 (0)