diff --git a/tests/logs.rs b/tests/logs.rs index 27c14e7..a8d9c5d 100644 --- a/tests/logs.rs +++ b/tests/logs.rs @@ -4,6 +4,7 @@ use test::prelude::*; struct EchoService<'a> { client: &'a TestKubeClient, pod: TemporaryResource<'a, Pod>, + pub logs_enabled: bool, } impl<'a> EchoService<'a> { @@ -46,7 +47,26 @@ impl<'a> EchoService<'a> { client.verify_pod_condition(&pod, "Ready"); - EchoService { client, pod } + const ANNOTATION_KEY_FEATURE_LOGS: &str = "featureLogs"; + + let logs_enabled = match client + .get_annotation::(&pod, ANNOTATION_KEY_FEATURE_LOGS) + .as_ref() + { + "true" => true, + "false" => false, + value => panic!( + "Pod annotation [{}] contains unknown value [{}]; \ + expected [true] or [false]", + ANNOTATION_KEY_FEATURE_LOGS, value, + ), + }; + + EchoService { + client, + pod, + logs_enabled, + } } pub fn get_logs(&self, params: &LogParams) -> Vec { @@ -62,7 +82,12 @@ fn all_logs_should_be_retrievable() { let echo_service = EchoService::new(&client, &log_output); let logs = echo_service.get_logs(&LogParams::default()); - assert_equals(&["line 1", "line 2", "line 3"], &logs); + + if echo_service.logs_enabled { + assert_equals(&["line 1", "line 2", "line 3"], &logs); + } else { + assert_that(&logs).is_empty(); + } } #[test] @@ -80,28 +105,35 @@ fn the_tail_of_logs_should_be_retrievable() { let logs = echo_service.get_logs(&with_tail_lines(0)); assert_that(&logs).is_empty(); - let logs = echo_service.get_logs(&with_tail_lines(1)); - assert_equals(&["line 3"], &logs); + if echo_service.logs_enabled { + let logs = echo_service.get_logs(&with_tail_lines(1)); + assert_equals(&["line 3"], &logs); - let logs = echo_service.get_logs(&with_tail_lines(2)); - assert_equals(&["line 2", "line 3"], &logs); + let logs = echo_service.get_logs(&with_tail_lines(2)); + assert_equals(&["line 2", "line 3"], &logs); - let logs = echo_service.get_logs(&with_tail_lines(3)); - assert_equals(&["line 1", "line 2", "line 3"], &logs); + let logs = echo_service.get_logs(&with_tail_lines(3)); + assert_equals(&["line 1", "line 2", "line 3"], &logs); - let logs = echo_service.get_logs(&with_tail_lines(4)); - assert_equals(&["line 1", "line 2", "line 3"], &logs); + let logs = echo_service.get_logs(&with_tail_lines(4)); + assert_equals(&["line 1", "line 2", "line 3"], &logs); + } } #[test] -fn non_ascii_characters_should_be_handled_correctly() { +fn non_ascii_characters_should_be_handled_correctly_in_the_logs() { let client = TestKubeClient::new(); let log_output = vec!["Spade: ♠", "Heart: ♥", "Diamond: ♦", "Club: ♣"]; let echo_service = EchoService::new(&client, &log_output); let logs = echo_service.get_logs(&LogParams::default()); - assert_equals(&["Spade: ♠", "Heart: ♥", "Diamond: ♦", "Club: ♣"], &logs); + + if echo_service.logs_enabled { + assert_equals(&["Spade: ♠", "Heart: ♥", "Diamond: ♦", "Club: ♣"], &logs); + } else { + assert_that(&logs).is_empty(); + } } fn assert_equals(expected: &[&str], actual: &[String]) { diff --git a/tests/test/kube.rs b/tests/test/kube.rs index 2260b40..211a440 100644 --- a/tests/test/kube.rs +++ b/tests/test/kube.rs @@ -124,7 +124,20 @@ impl TestKubeClient { }) } - /// Verifies that the given pod condition becomes true within 30 seconds. + /// Returns the value of an annotation for the given resource. + pub fn get_annotation(&self, resource: &K, key: &str) -> String + where + K: Clone + DeserializeOwned + Meta, + { + self.runtime.block_on(async { + self.kube_client + .get_annotation(resource, key) + .await + .expect("Annotation could not be retrieved") + }) + } + + /// Verifies that the given pod condition becomes true within the specified timeout. pub fn verify_pod_condition(&self, pod: &Pod, condition_type: &str) { self.runtime.block_on(async { self.kube_client @@ -161,6 +174,7 @@ pub struct Timeouts { pub apply_crd: Duration, pub create: Duration, pub delete: Duration, + pub get_annotation: Duration, pub verify_pod_condition: Duration, } @@ -170,6 +184,7 @@ impl Default for Timeouts { apply_crd: Duration::from_secs(30), create: Duration::from_secs(10), delete: Duration::from_secs(10), + get_annotation: Duration::from_secs(10), verify_pod_condition: Duration::from_secs(30), } } @@ -224,7 +239,6 @@ impl KubeClient { let mut stream = crds.watch(&lp, "0").await?.boxed(); while let Some(status) = stream.try_next().await? { - println!("{:?}", status); if let WatchEvent::Modified(crd) = status { if is_ready(&crd) { return Ok(()); @@ -325,7 +339,48 @@ impl KubeClient { )) } - /// Verifies that the given pod condition becomes true within 30 seconds. + /// Returns the value of an annotation for the given resource. + pub async fn get_annotation(&self, resource: &K, key: &str) -> Result + where + K: Clone + DeserializeOwned + Meta, + { + let get_value = |resource: &K| { + resource + .meta() + .annotations + .as_ref() + .and_then(|annotations| annotations.get(key).cloned()) + }; + + let timeout_secs = self.timeouts.get_annotation.as_secs() as u32; + let api: Api = Api::namespaced(self.client.clone(), &self.namespace); + + let lp = ListParams::default() + .fields(&format!("metadata.name={}", resource.name())) + .timeout(timeout_secs); + let mut stream = api.watch(&lp, "0").await?.boxed(); + + if let Some(value) = get_value(&resource) { + return Ok(value); + } + + while let Some(event) = stream.try_next().await? { + if let WatchEvent::Added(resource) = event { + if let Some(value) = get_value(&resource) { + return Ok(value); + } + } + } + + Err(anyhow!( + "Annotation [{}] could not be retrieved from [{}] within {} seconds", + key, + resource.name(), + timeout_secs + )) + } + + /// Verifies that the given pod condition becomes true within the specified timeout. pub async fn verify_pod_condition(&self, pod: &Pod, condition_type: &str) -> Result<()> { let is_condition_true = |pod: &Pod| { get_pod_conditions(pod)