Skip to content
This repository was archived by the owner on Dec 21, 2021. It is now read-only.

Test "kubectl logs" #9

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ serde_json = "1.0"
serde_yaml = "0.8"
spectral = "0.6"
tokio = { version = "1.5", features = ["rt-multi-thread"] }
uuid = { version = "0.8", features = ["v4"] }
111 changes: 111 additions & 0 deletions tests/logs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
mod test;
use test::prelude::*;

struct EchoService<'a> {
client: &'a TestKubeClient,
pod: TemporaryResource<'a, Pod>,
}

impl<'a> EchoService<'a> {
pub fn new(client: &'a TestKubeClient, log_output: &[&str]) -> Self {
setup_repository(&client);

/// Newline character for LOG_OUTPUT
///
/// Source code: \\\\\\\\n
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting this, I can only imagine the pain that went into these five lines!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the layers which eat backslashes are under our control. We could try to make them less voracious.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not worth the effort unless it creates issues.

/// Pod spec: \\\\n
/// Systemd unit file: \\n
/// echo-service: \n
/// Journal: separate entries
const NEWLINE: &str = "\\\\\\\\n";

let pod = TemporaryResource::new(
&client,
&formatdoc! {r#"
apiVersion: v1
kind: Pod
metadata:
name: agent-logs-integration-test-{id}
spec:
containers:
- name: echo-service
image: echo-service:1.0.0
command:
- echo-service-1.0.0/start.sh
env:
- name: LOG_OUTPUT
value: "{log_output}"
tolerations:
- key: kubernetes.io/arch
operator: Equal
value: stackable-linux
"#,
id = Uuid::new_v4(),
log_output = log_output.join(NEWLINE)
},
);

client.verify_pod_condition(&pod, "Ready");

EchoService { client, pod }
}

pub fn get_logs(&self, params: &LogParams) -> Vec<String> {
self.client.get_logs(&self.pod, params)
}
}

#[test]
fn all_logs_should_be_retrievable() {
let client = TestKubeClient::new();

let log_output = vec!["line 1", "line 2", "line 3"];
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);
}

#[test]
fn the_tail_of_logs_should_be_retrievable() {
let client = TestKubeClient::new();

let log_output = vec!["line 1", "line 2", "line 3"];
let echo_service = EchoService::new(&client, &log_output);

let with_tail_lines = |tail_lines| LogParams {
tail_lines: Some(tail_lines),
..Default::default()
};

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);

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(4));
assert_equals(&["line 1", "line 2", "line 3"], &logs);
}

#[test]
fn non_ascii_characters_should_be_handled_correctly() {
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);
}

fn assert_equals(expected: &[&str], actual: &[String]) {
assert_that(&actual.iter().map(String::as_ref).collect::<Vec<_>>())
.equals_iterator(&expected.iter());
}
62 changes: 21 additions & 41 deletions tests/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,27 @@ use test::prelude::*;
fn service_should_be_started_successfully() {
let client = TestKubeClient::new();

create_repository(&client);

// Remove pod if it still exists from a previous test run.
if let Some(pod) = client.find::<Pod>("agent-integration-test") {
client.delete(pod);
};

let pod = client.create(indoc! {r#"
apiVersion: v1
kind: Pod
metadata:
name: agent-integration-test
spec:
containers:
- name: test-service
image: test-service:0.1.0
command:
- test-service-0.1.0/start.sh
tolerations:
- key: kubernetes.io/arch
operator: Equal
value: stackable-linux
"#});
setup_repository(&client);

let pod = TemporaryResource::new(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have removed the check for an existing object - which I understand to be due to the new usage of TemporaryResource - but I'm fairly sure there are scenarios where cleanup might fail.
Would it perhaps make sense to add the check for and removal of old objects to TemporaryObject? Could be split up in new() and new_and_remove_if_exists() or something similar in case a test ever needs the distinction..

Copy link
Member Author

@siegfriedweber siegfriedweber Apr 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was also my first thought. But if it is not possible to delete a resource for the first time why should it be possible to delete it the second (or third) time? Sure, time passes and the system could have changed to allow the deletion meanwhile, but when do you stop trying?

In the former code the resource was not deleted if the test case failed/panicked. So it was necessary to do the check. With the introduction of TemporaryResource I never encountered this problem anymore.

One remaining problem is that system signals like SIGINT are not handled. If the user presses Ctrl+C while the tests are running, chances are high that pods remain. As the pods often have unique names with UUIDs included, the former existence check would not help. It would be necessary to implement correct signal handling, e.g. with the ctrlc crate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main scenario that I considered was network trouble that makes Kubernetes briefly unavailable during the test which causes the test to fail without removing the resource.
Same as with the comment above though, happy to leave as is and see if it ever becomes an issue.

&client,
indoc! {"
apiVersion: v1
kind: Pod
metadata:
name: agent-service-integration-test
spec:
containers:
- name: noop-service
image: noop-service:1.0.0
command:
- noop-service-1.0.0/start.sh
tolerations:
- key: kubernetes.io/arch
operator: Equal
value: stackable-linux
"},
);

client.verify_pod_condition(&pod, "Ready");

client.delete(pod);
}

fn create_repository(client: &TestKubeClient) {
client.apply_crd(&Repository::crd());

client.apply::<Repository>(indoc!("
apiVersion: stable.stackable.de/v1
kind: Repository
metadata:
name: integration-test-repository
namespace: default
spec:
repo_type: StackableRepo
properties:
url: https://raw.githubusercontent.com/stackabletech/integration-test-repo/6d784f1fb433123cb3b1d5cd7364a4553246d749/
"));
}
Loading