Skip to content

Truncate K8s event message if it is too long #327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 18, 2022
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Changed
- Reported K8s events are now limited to 1024 bytes ([#327]).

### Removed
- `Client::set_condition` ([#326]).
- `Error` variants that are no longer used ([#326]).

[#326]: https://github.com/stackabletech/operator-rs/pull/326
[#327]: https://github.com/stackabletech/operator-rs/pull/327

## [0.11.0] - 2022-02-17

Expand Down
70 changes: 69 additions & 1 deletion src/logging/k8s_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use super::controller::ReconcilerError;
/// Converts an [`Error`] into a publishable Kubernetes [`Event`]
fn error_to_event<E: ReconcilerError>(err: &E) -> Event {
// Walk the whole error chain, so that we get all the full reason for the error
let full_msg = {
let mut full_msg = {
use std::fmt::Write;
let mut buf = err.to_string();
let mut err: &dyn Error = err;
Expand All @@ -28,6 +28,7 @@ fn error_to_event<E: ReconcilerError>(err: &E) -> Event {
}
}
};
message::truncate_with_ellipsis(&mut full_msg, 1024);
Event {
type_: EventType::Warning,
reason: err.category().to_string(),
Expand Down Expand Up @@ -77,6 +78,73 @@ pub fn publish_controller_error_as_k8s_event<ReconcileErr, QueueErr>(
);
}

mod message {
/// Ensures that `msg` is at most `max_len` _bytes_ long
///
/// If `msg` is longer than `max_len` then the extra text is replaced with an ellipsis.
pub fn truncate_with_ellipsis(msg: &mut String, max_len: usize) {
const ELLIPSIS: char = '…';
const ELLIPSIS_LEN: usize = ELLIPSIS.len_utf8();
let len = msg.len();
if len > max_len {
msg.truncate(find_start_of_char(msg, len.saturating_sub(ELLIPSIS_LEN)));
if ELLIPSIS_LEN <= max_len {
msg.push(ELLIPSIS);
}
}
}

fn find_start_of_char(s: &str, mut pos: usize) -> usize {
loop {
if s.is_char_boundary(pos) {
break pos;
}
pos -= 1;
}
}

#[cfg(test)]
mod tests {
use crate::logging::k8s_events::message::find_start_of_char;

use super::truncate_with_ellipsis;

#[test]
fn truncate_should_be_noop_if_string_fits() {
let mut x = "hello".to_string();
truncate_with_ellipsis(&mut x, 5);
assert_eq!(&x, "hello");
}

#[test]
fn truncate_should_ellipsize_large_string() {
let mut x = "hello".to_string();
truncate_with_ellipsis(&mut x, 4);
assert_eq!(&x, "he…");
}

#[test]
fn truncate_should_ellipsize_emoji() {
let mut x = "hello🙋".to_string();
truncate_with_ellipsis(&mut x, 8);
assert_eq!(&x, "hello…");
}

#[test]
fn find_start_of_char_should_be_noop_for_ascii() {
assert_eq!(find_start_of_char("hello", 2 /* l */), 2);
}

#[test]
fn find_start_of_char_should_find_start_of_emoji() {
assert_eq!(
find_start_of_char("hello🙋", 7 /* in the middle of the emoji */),
5
);
}
}
}

#[cfg(test)]
mod tests {
use k8s_openapi::api::core::v1::ConfigMap;
Expand Down