From 447d9bb2b2de806e15dfb994559b7900dee4ecf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Herbel?= Date: Mon, 25 Nov 2024 11:15:47 +0100 Subject: [PATCH] Implement option to configure secret environment variables for RF executions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specific use case: CryptoLibrary for encrypting login data.  CMK-19115 --- examples/termination/main.rs | 2 + src/bin/scheduler/internal_config.rs | 7 +++ src/config.rs | 7 +++ src/rf/robot.rs | 69 ++++++++++++++++++++++++++-- tests/test_ht_import_scheduler.rs | 1 + tests/test_plan_run.rs | 2 + tests/test_scheduler.rs | 4 ++ 7 files changed, 89 insertions(+), 3 deletions(-) diff --git a/examples/termination/main.rs b/examples/termination/main.rs index d3b2bf9c..768aa7ae 100755 --- a/examples/termination/main.rs +++ b/examples/termination/main.rs @@ -46,6 +46,7 @@ fn system_main() -> AnyhowResult<()> { "--variable".into(), format!("RESOURCE:{resource_file}"), ], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Complete, }; let token = CancellationToken::new(); @@ -100,6 +101,7 @@ fn rcc_main(rcc_binary_path: Utf8PathBuf) -> AnyhowResult<()> { "--variable".into(), format!("RESOURCE:{resource_file}"), ], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Complete, }; let rcc_environment = Environment::Rcc(RCCEnvironment { diff --git a/src/bin/scheduler/internal_config.rs b/src/bin/scheduler/internal_config.rs index ac2bae7a..174fe34a 100644 --- a/src/bin/scheduler/internal_config.rs +++ b/src/bin/scheduler/internal_config.rs @@ -133,6 +133,9 @@ pub fn from_external_config( .map(|f| plan_source_dir.join(f)) .collect(), exit_on_failure: plan_config.robot_config.exit_on_failure, + secret_environment_variables: plan_config + .robot_config + .secret_environment_variables, }, plan_config.execution_config.n_attempts_max, plan_config.execution_config.retry_strategy, @@ -200,6 +203,7 @@ mod tests { variable_files: vec![], argument_files: vec!["args.txt".into(), "more_args.txt".into()], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, @@ -235,6 +239,7 @@ mod tests { variable_files: vec!["vars.txt".into()], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, @@ -317,6 +322,7 @@ mod tests { "--variablefile".into(), "/synthetic_tests/rcc/vars.txt".into() ], + secret_env_vars: vec![], n_attempts_max: 1, retry_strategy: RetryStrategy::Complete, } @@ -378,6 +384,7 @@ mod tests { "--argumentfile".into(), "/synthetic_tests/system/more_args.txt".into() ], + secret_env_vars: vec![], n_attempts_max: 1, retry_strategy: RetryStrategy::Incremental, } diff --git a/src/config.rs b/src/config.rs index fa2e20d3..c5503803 100644 --- a/src/config.rs +++ b/src/config.rs @@ -78,6 +78,7 @@ pub struct RobotConfig { pub variable_files: Vec, pub argument_files: Vec, pub exit_on_failure: bool, + pub secret_environment_variables: Vec, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -86,6 +87,12 @@ pub struct RobotFrameworkVariable { pub value: String, } +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct RobotFrameworkSecretEnvVar { + pub name: String, + pub value: String, +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct ExecutionConfig { pub n_attempts_max: usize, diff --git a/src/rf/robot.rs b/src/rf/robot.rs index c538550f..e4222fc2 100644 --- a/src/rf/robot.rs +++ b/src/rf/robot.rs @@ -9,6 +9,7 @@ pub const PYTHON_EXECUTABLE: &str = "python"; pub struct Robot { pub robot_target: Utf8PathBuf, pub command_line_args: Vec, + pub secret_env_vars: Vec<(String, String)>, pub n_attempts_max: usize, pub retry_strategy: RetryStrategy, } @@ -28,6 +29,11 @@ impl Robot { ) -> Self { Self { robot_target: robot_config.robot_target.clone(), + secret_env_vars: robot_config + .secret_environment_variables + .iter() + .map(|var| (var.name.clone(), var.value.clone())) + .collect(), command_line_args: Self::config_to_command_line_args(robot_config), n_attempts_max, retry_strategy, @@ -74,6 +80,9 @@ impl Robot { .add_argument("--report") .add_argument("NONE") .add_argument(&self.robot_target); + for (k, v) in &self.secret_env_vars { + command_spec.add_secret_env(k, v); + } command_spec } @@ -121,10 +130,10 @@ impl Robot { #[cfg(test)] mod tests { use super::*; - use crate::config::RobotFrameworkVariable; + use crate::config::{RobotFrameworkSecretEnvVar, RobotFrameworkVariable}; #[test] - fn test_command_line_args_empty() { + fn test_new_command_line_args_empty() { assert!(Robot::new( RobotConfig { robot_target: "/suite/tasks.robot".into(), @@ -137,6 +146,7 @@ mod tests { variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![] }, 1, RetryStrategy::Incremental @@ -146,7 +156,7 @@ mod tests { } #[test] - fn test_command_line_args_non_empty() { + fn test_new_command_line_args_non_empty() { assert_eq!( Robot::new( RobotConfig { @@ -175,6 +185,7 @@ mod tests { "/suite/argfile2.txt".into() ], exit_on_failure: true, + secret_environment_variables: vec![], }, 1, RetryStrategy::Incremental @@ -216,6 +227,34 @@ mod tests { ); } + #[test] + fn test_new_secret_env_vars() { + assert_eq!( + Robot::new( + RobotConfig { + robot_target: "/suite/tasks.robot".into(), + top_level_suite_name: None, + suites: vec![], + tests: vec![], + test_tags_include: vec![], + test_tags_exclude: vec![], + variables: vec![], + variable_files: vec![], + argument_files: vec![], + exit_on_failure: false, + secret_environment_variables: vec![RobotFrameworkSecretEnvVar { + name: "NAME".into(), + value: "value".into() + }] + }, + 1, + RetryStrategy::Incremental + ) + .secret_env_vars, + vec![("NAME".into(), "value".into())] + ); + } + #[test] fn create_complete_command_spec() { // Assemble @@ -228,6 +267,7 @@ mod tests { "--variable".into(), "k:v".into(), ], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Complete, }; let output_directory = @@ -267,6 +307,7 @@ mod tests { "top_suite".into(), "--exitonfailure".into(), ], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Incremental, }; let output_directory = @@ -301,6 +342,7 @@ mod tests { robot_target: "~/calculator_test/calculator.robot".into(), n_attempts_max: 2, command_line_args: vec![], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Incremental, }; let output_directory = @@ -327,6 +369,26 @@ mod tests { assert_eq!(command_spec, expected) } + #[test] + fn create_command_secrent_env_vars() { + assert_eq!( + Robot { + robot_target: "~/calculator_test/calculator.robot".into(), + n_attempts_max: 1, + command_line_args: vec![], + secret_env_vars: vec![("NAME".into(), "value".into())], + retry_strategy: RetryStrategy::Complete, + } + .command_spec( + &Utf8PathBuf::default(), + &Utf8PathBuf::default().join("out.xml"), + 1 + ) + .secret_envs, + vec![("NAME".into(), "value".into())] + ) + } + #[test] fn create_two_attempts() { // Assemble @@ -334,6 +396,7 @@ mod tests { robot_target: "~/calculator_test/calculator.robot".into(), n_attempts_max: 2, command_line_args: vec![], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Incremental, }; let output_directory = diff --git a/tests/test_ht_import_scheduler.rs b/tests/test_ht_import_scheduler.rs index 70a080b8..1c8aebbc 100644 --- a/tests/test_ht_import_scheduler.rs +++ b/tests/test_ht_import_scheduler.rs @@ -97,6 +97,7 @@ fn create_config(test_dir: &Utf8Path, suite_dir: &Utf8Path, rcc_config: RCCConfi variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, diff --git a/tests/test_plan_run.rs b/tests/test_plan_run.rs index d5cade7b..220175b0 100644 --- a/tests/test_plan_run.rs +++ b/tests/test_plan_run.rs @@ -17,6 +17,7 @@ fn test_rebot_run() -> AnyhowResult<()> { robot_target: "tests/minimal_suite/tasks.robot".into(), n_attempts_max: 1, command_line_args: vec![], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Complete, }; let (attempt_reports, rebot) = run_attempts_with_rebot( @@ -45,6 +46,7 @@ fn test_timeout_process() -> AnyhowResult<()> { robot_target: "tests/timeout/tasks.robot".into(), n_attempts_max: 1, command_line_args: vec!["--variable".into(), format!("RESOURCE:{resource}")], + secret_env_vars: vec![], retry_strategy: RetryStrategy::Complete, }; let (attempt_reports, rebot) = run_attempts_with_rebot( diff --git a/tests/test_scheduler.rs b/tests/test_scheduler.rs index 699113f7..6e326b25 100644 --- a/tests/test_scheduler.rs +++ b/tests/test_scheduler.rs @@ -177,6 +177,7 @@ fn create_config( variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, @@ -216,6 +217,7 @@ fn create_config( variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, @@ -259,6 +261,7 @@ fn create_config( variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1, @@ -310,6 +313,7 @@ fn create_config( variable_files: vec![], argument_files: vec![], exit_on_failure: false, + secret_environment_variables: vec![], }, execution_config: ExecutionConfig { n_attempts_max: 1,