Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
725b257
containerdebug CRD and validation logic
Maleware Oct 21, 2026
3dd760c
Add changelog entry
Maleware Oct 21, 2026
af5da2d
Merge branch 'main' into spike/containerdebug-switch
Maleware Oct 21, 2026
6e64b74
Moving containerdebug to stackable-operator/src/crd
Maleware Oct 21, 2026
88f5516
Merge branch 'spike/containerdebug-switch' of https://github.com/stac…
Maleware Oct 21, 2026
ad1773a
fixing changelog
Maleware Oct 23, 2026
e14da98
Formatting
Maleware Oct 23, 2026
35d6df8
Merge branch 'main' into spike/containerdebug-switch
Maleware Oct 23, 2026
ca0f55d
update to interval non 0
Maleware Oct 29, 2026
aa62a4f
Merge branch 'spike/containerdebug-switch' of https://github.com/stac…
Maleware Oct 29, 2026
bd94d1d
Merge branch 'main' into spike/containerdebug-switch
Maleware Oct 29, 2026
cf8f955
fix Changelog
Maleware Oct 29, 2026
d61d980
Apply code review (typos etc)
Maleware Oct 30, 2026
20418d2
Removing Debug from struct ContainerDebugConfig
Maleware Oct 30, 2026
00fe8b8
Merge branch 'spike/containerdebug-switch' of https://github.com/stac…
Maleware Oct 30, 2026
4669ece
Merge branch 'main' into spike/containerdebug-switch
Maleware Oct 30, 2026
7342fc5
rename to ContainerdebugConfig
Maleware Oct 30, 2026
f7ea367
Merge branch 'spike/containerdebug-switch' of https://github.com/stac…
Maleware Oct 30, 2026
4feff0d
correct cargodocs
Maleware Oct 30, 2026
9831334
rustfmt
Maleware Oct 30, 2026
09f703e
Add crds to dummy.rs
Maleware Oct 30, 2026
24f1b97
fix typo
Maleware Oct 30, 2026
ca4c6a7
add debug again
Maleware Oct 30, 2026
d7d661b
regenerate dummy crds
Maleware Oct 30, 2026
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
6 changes: 6 additions & 0 deletions crates/stackable-operator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- Add `crd::containerdebug.rs` module with the `ContainerdebugConfig` CRD struct as well as helper functions on `ValidatedContainerdebugConfig` ([#1308])

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

### Changed

- BREAKING: Bump the RustCrypto dependencies to their new generation, which affects the re-exported
Expand Down
40 changes: 40 additions & 0 deletions crates/stackable-operator/crds/DummyCluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,26 @@ spec:
config:
default: {}
properties:
containerdebug:
default:
enabled: null
interval: null
description: |-
Configures the `containerdebug` tool, which periodically logs information about the container
environment (such as network interfaces, DNS lookups and disk usage) in the background of the
product container.
properties:
enabled:
description: Whether `containerdebug` runs in the background of the product container. Defaults to `true`.
nullable: true
type: boolean
interval:
description: |-
Time between two runs of `containerdebug`, e.g. `30m`, `1h` or `2d`. Must not be zero.
Defaults to `30m`.
nullable: true
type: string
type: object
logging:
default:
containers: {}
Expand Down Expand Up @@ -1114,6 +1134,26 @@ spec:
config:
default: {}
properties:
containerdebug:
default:
enabled: null
interval: null
description: |-
Configures the `containerdebug` tool, which periodically logs information about the container
environment (such as network interfaces, DNS lookups and disk usage) in the background of the
product container.
properties:
enabled:
description: Whether `containerdebug` runs in the background of the product container. Defaults to `true`.
nullable: true
type: boolean
interval:
description: |-
Time between two runs of `containerdebug`, e.g. `30m`, `1h` or `2d`. Must not be zero.
Defaults to `30m`.
nullable: true
type: string
type: object
logging:
default:
containers: {}
Expand Down
168 changes: 168 additions & 0 deletions crates/stackable-operator/src/crd/containerdebug/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//! Configuration of the `containerdebug` tool, which runs in the background of product containers
//! and periodically logs information about the container environment.
//!
//! Learn more in the
//! [container environment documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/observability/containerdebug).

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use snafu::{Snafu, ensure};
use stackable_shared::time::Duration;

use crate::{
config::{fragment::Fragment, merge::Merge},
versioned::versioned,
};

/// The interval used if the user does not configure one.
pub const DEFAULT_INTERVAL: Duration = Duration::from_minutes_unchecked(30);

#[derive(Debug, Eq, PartialEq, Snafu)]
pub enum Error {
#[snafu(display("the containerdebug interval must not be zero"))]
IntervalZero,
}

#[versioned(version(name = "v1alpha1"))]
pub mod versioned {
/// Configures the `containerdebug` tool, which periodically logs information about the container
/// environment (such as network interfaces, DNS lookups and disk usage) in the background of the
/// product container.
#[derive(Clone, Debug, Eq, Fragment, JsonSchema, PartialEq)]
#[fragment(path_overrides(fragment = "crate::config::fragment"))]
#[fragment_attrs(
derive(
Clone,
Debug,
Default,
Deserialize,
Eq,
JsonSchema,
Merge,
PartialEq,
Serialize
),
merge(path_overrides(merge = "crate::config::merge")),
serde(rename_all = "camelCase")
)]
pub struct ContainerdebugConfig {
/// Whether `containerdebug` runs in the background of the product container. Defaults to `true`.
pub enabled: bool,

/// Time between two runs of `containerdebug`, e.g. `30m`, `1h` or `2d`. Must not be zero.
/// Defaults to `30m`.
pub interval: Duration,
}
}

impl v1alpha1::ContainerdebugConfig {
/// Validates the merged configuration.
///
/// The interval is only validated if `containerdebug` is enabled, so that a role group can
/// disable it without failing on an interval inherited from its role.
pub fn validate(&self) -> Result<ValidatedContainerdebugConfig, Error> {
if !self.enabled {
return Ok(ValidatedContainerdebugConfig::Disabled);
}

// `containerdebug` schedules the next run by adding the interval to the start of the
// previous one, so an interval of zero would collect (including DNS lookups) in a tight loop.
ensure!(!self.interval.is_zero(), IntervalZeroSnafu);

Ok(ValidatedContainerdebugConfig::Enabled {
interval: self.interval,
})
}
}

/// A validated [`v1alpha1::ContainerdebugConfig`].
///
/// Unlike the user-facing configuration, this cannot carry an interval while `containerdebug` is
/// disabled, nor an interval of zero.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ValidatedContainerdebugConfig {
Disabled,
Enabled { interval: Duration },
}

impl ValidatedContainerdebugConfig {
/// Returns the shell command which starts `containerdebug` in the background, or [`None`] if
/// it is disabled.
///
/// The collected state is written as JSON to `containerdebug-state.json` in `log_dir`.
pub fn background_command(&self, log_dir: &str) -> Option<String> {
match self {
Self::Disabled => None,
// `containerdebug` requires the `=` between `--loop` and its value.
Self::Enabled { interval } => Some(format!(
"containerdebug --output={log_dir}/containerdebug-state.json --loop={interval} &"
)),
}
}
}

/// Returns the default `containerdebug` configuration, which operators merge underneath the
/// role and role group configuration.
pub fn default_containerdebug_config() -> v1alpha1::ContainerdebugConfigFragment {
v1alpha1::ContainerdebugConfigFragment {
enabled: Some(true),
interval: Some(DEFAULT_INTERVAL),
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use rstest::rstest;

use super::*;
use crate::config::fragment;

const LOG_DIR: &str = "/stackable/log";

fn config(enabled: bool, interval: &str) -> v1alpha1::ContainerdebugConfig {
v1alpha1::ContainerdebugConfig {
enabled,
interval: Duration::from_str(interval).expect("valid duration"),
}
}

#[test]
fn defaults_render_command() {
let config: v1alpha1::ContainerdebugConfig =
fragment::validate(default_containerdebug_config()).expect("defaults are complete");

assert_eq!(
config
.validate()
.expect("defaults are valid")
.background_command(LOG_DIR),
Some(
"containerdebug --output=/stackable/log/containerdebug-state.json --loop=30m &"
.to_owned()
)
);
}

#[rstest]
#[case("0s", Err(Error::IntervalZero))]
#[case("1s", Ok(ValidatedContainerdebugConfig::Enabled { interval: Duration::from_secs(1) }))]
fn validate_rejects_zero_interval(
#[case] interval: &str,
#[case] expected: Result<ValidatedContainerdebugConfig, Error>,
) {
assert_eq!(config(true, interval).validate(), expected);
}

/// A role group can disable `containerdebug` without failing on an interval inherited from
/// its role.
#[test]
fn disabled_ignores_interval_and_renders_nothing() {
let validated = config(false, "0s")
.validate()
.expect("the interval is not validated while disabled");

assert_eq!(validated.background_command(LOG_DIR), None);
}
}
1 change: 1 addition & 0 deletions crates/stackable-operator/src/crd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

pub mod authentication;
pub mod containerdebug;
pub mod git_sync;
pub mod listener;
pub mod openlineage;
Expand Down
5 changes: 4 additions & 1 deletion crates/xtask/src/crd/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub mod versioned {
>,
}

#[derive(Debug, Default, PartialEq, Fragment, JsonSchema)]
#[derive(Debug, PartialEq, Fragment, JsonSchema)]
#[fragment_attrs(
derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema),
schemars(crate = "stackable_operator::schemars"),
Expand All @@ -130,6 +130,9 @@ pub mod versioned {

#[fragment_attrs(serde(default))]
pub logging: stackable_operator::product_logging::spec::Logging<Container>,

#[fragment_attrs(serde(default))]
pub containerdebug: stackable_operator::crd::containerdebug::v1alpha1::ContainerdebugConfig,
}

#[derive(Debug, Default, PartialEq, Fragment, JsonSchema)]
Expand Down
Loading