|
| 1 | +use std::time::Duration; |
| 2 | + |
| 3 | +use anyhow::anyhow; |
| 4 | +use oci_spec::runtime::{HookBuilder, HooksBuilder, ProcessBuilder, SpecBuilder}; |
| 5 | +use test_framework::{Test, TestGroup, TestResult}; |
| 6 | + |
| 7 | +use crate::utils::{ |
| 8 | + CreateOptions, LifecycleStatus, WaitTarget, create_container, delete_container, generate_uuid, |
| 9 | + kill_container, prepare_bundle, set_config, start_container, wait_for_state, |
| 10 | +}; |
| 11 | + |
| 12 | +const STATE_WAIT_TIMEOUT_SECS: u64 = 5; |
| 13 | +const STATE_POLL_INTERVAL_MILLIS: u64 = 100; |
| 14 | + |
| 15 | +fn wait_for_target(id: &str, bundle_path: &std::path::Path, target: WaitTarget) { |
| 16 | + wait_for_state( |
| 17 | + id, |
| 18 | + bundle_path, |
| 19 | + target, |
| 20 | + Duration::from_secs(STATE_WAIT_TIMEOUT_SECS), |
| 21 | + Duration::from_millis(STATE_POLL_INTERVAL_MILLIS), |
| 22 | + ) |
| 23 | + .unwrap(); |
| 24 | +} |
| 25 | + |
| 26 | +fn run_hook_env_test( |
| 27 | + shell_condition: &str, |
| 28 | + process_env: Vec<String>, |
| 29 | + hook_env: Option<Vec<String>>, |
| 30 | +) -> TestResult { |
| 31 | + let id = generate_uuid(); |
| 32 | + let id_str = id.to_string(); |
| 33 | + let bundle = prepare_bundle().unwrap(); |
| 34 | + |
| 35 | + let shell_cmd = format!("if {shell_condition}; then exit 0; else exit 1; fi"); |
| 36 | + let hook_args = vec!["sh".to_string(), "-c".to_string(), shell_cmd]; |
| 37 | + let hook = if let Some(env) = hook_env { |
| 38 | + HookBuilder::default() |
| 39 | + .path("/bin/sh") |
| 40 | + .args(hook_args) |
| 41 | + .env(env) |
| 42 | + } else { |
| 43 | + HookBuilder::default().path("/bin/sh").args(hook_args) |
| 44 | + }; |
| 45 | + |
| 46 | + let mut process = ProcessBuilder::default() |
| 47 | + .args(vec!["true".to_string()]) |
| 48 | + .build() |
| 49 | + .unwrap(); |
| 50 | + let mut env = process.env().clone().unwrap(); |
| 51 | + for e in process_env { |
| 52 | + env.push(e); |
| 53 | + } |
| 54 | + process.set_env(Some(env)); |
| 55 | + |
| 56 | + let spec = SpecBuilder::default() |
| 57 | + .process(process) |
| 58 | + .hooks( |
| 59 | + HooksBuilder::default() |
| 60 | + .start_container(vec![ |
| 61 | + hook.build().expect("could not build startContainer hook"), |
| 62 | + ]) |
| 63 | + .build() |
| 64 | + .expect("could not build hooks"), |
| 65 | + ) |
| 66 | + .build() |
| 67 | + .unwrap(); |
| 68 | + set_config(&bundle, &spec).unwrap(); |
| 69 | + |
| 70 | + create_container(&id_str, &bundle, &CreateOptions::default()) |
| 71 | + .unwrap() |
| 72 | + .wait() |
| 73 | + .unwrap(); |
| 74 | + wait_for_target( |
| 75 | + &id_str, |
| 76 | + bundle.path(), |
| 77 | + WaitTarget::Status(LifecycleStatus::Created), |
| 78 | + ); |
| 79 | + let start_result = |
| 80 | + start_container(&id_str, &bundle).and_then(|mut child| child.wait().map_err(Into::into)); |
| 81 | + let test_passed = match &start_result { |
| 82 | + Ok(status) => status.success(), |
| 83 | + Err(_) => false, |
| 84 | + }; |
| 85 | + |
| 86 | + let _ = kill_container(&id_str, &bundle).and_then(|mut c| c.wait().map_err(Into::into)); |
| 87 | + let _ = delete_container(&id_str, &bundle).and_then(|mut c| c.wait().map_err(Into::into)); |
| 88 | + let _ = wait_for_state( |
| 89 | + &id_str, |
| 90 | + bundle.path(), |
| 91 | + WaitTarget::Deleted, |
| 92 | + Duration::from_secs(STATE_WAIT_TIMEOUT_SECS), |
| 93 | + Duration::from_millis(STATE_POLL_INTERVAL_MILLIS), |
| 94 | + ); |
| 95 | + |
| 96 | + if test_passed { |
| 97 | + TestResult::Passed |
| 98 | + } else { |
| 99 | + TestResult::Failed(anyhow!( |
| 100 | + "startContainer hook env check failed — hook exited non-zero \ |
| 101 | + (start result: {:?})", |
| 102 | + start_result |
| 103 | + )) |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +fn get_test_inherit_env() -> Test { |
| 108 | + Test::new( |
| 109 | + "start_container_env_inherit", |
| 110 | + Box::new(|| { |
| 111 | + run_hook_env_test( |
| 112 | + r#"test "$ONE" = "two" && test "$FOO" = "bar""#, |
| 113 | + vec!["ONE=two".to_string(), "FOO=bar".to_string()], |
| 114 | + None, |
| 115 | + ) |
| 116 | + }), |
| 117 | + ) |
| 118 | +} |
| 119 | + |
| 120 | +fn get_test_explicit_env() -> Test { |
| 121 | + Test::new( |
| 122 | + "start_container_env_explicit", |
| 123 | + Box::new(|| { |
| 124 | + run_hook_env_test( |
| 125 | + r#"test "$HOOK_VAR" = "hook_value" && test -z "$PROC_VAR""#, |
| 126 | + vec!["PROC_VAR=should_not_appear".to_string()], |
| 127 | + Some(vec!["HOOK_VAR=hook_value".to_string()]), |
| 128 | + ) |
| 129 | + }), |
| 130 | + ) |
| 131 | +} |
| 132 | + |
| 133 | +pub fn get_start_container_env_tests() -> TestGroup { |
| 134 | + let mut tg = TestGroup::new("start_container_env"); |
| 135 | + tg.add(vec![ |
| 136 | + Box::new(get_test_inherit_env()), |
| 137 | + Box::new(get_test_explicit_env()), |
| 138 | + ]); |
| 139 | + tg |
| 140 | +} |
0 commit comments