From 9c54e4f4183597c1e24ce7bbaaf8d44d1fcba9fd Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Fri, 13 Nov 2026 09:55:11 +0100 Subject: [PATCH 1/6] add trait IntoValidated for validating configs --- crates/stackable-operator/src/config/fragment.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index a1a89f6be..2cf2d4fb7 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -19,6 +19,17 @@ use super::merge::Merge; #[cfg(doc)] use crate::role_utils::{Role, RoleGroup}; +/// Turns a role group's merged config into the validated config the build step works with. +trait IntoValidated { + /// `ValidatedConfig` of the `Config` to validate. + type Validated; + /// Error thrown when failed to validate a `Config`. + type ConfigError; + + /// Consumes the merged config, as nothing needs it once it is validated. + fn validate(self) -> Result; +} + /// Contains context used for generating validation errors /// /// Constructed internally in [`validate`] From f9d55e47ba09be6329ee7e3304a35bdde795369a Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Fri, 13 Nov 2026 10:11:26 +0100 Subject: [PATCH 2/6] Allow trait not being used --- crates/stackable-operator/src/config/fragment.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index 2cf2d4fb7..b790e37f3 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -20,7 +20,8 @@ use super::merge::Merge; use crate::role_utils::{Role, RoleGroup}; /// Turns a role group's merged config into the validated config the build step works with. -trait IntoValidated { +#[allow(dead_code)] +pub trait IntoValidated { /// `ValidatedConfig` of the `Config` to validate. type Validated; /// Error thrown when failed to validate a `Config`. From 59274abd2541c0b166c50b16849e6120861c4c40 Mon Sep 17 00:00:00 2001 From: Maximilian Wittich <56642549+Maleware@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:51:40 +0100 Subject: [PATCH 3/6] Reword comment Co-authored-by: Techassi --- crates/stackable-operator/src/config/fragment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index b790e37f3..f571f39c5 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -25,10 +25,10 @@ pub trait IntoValidated { /// `ValidatedConfig` of the `Config` to validate. type Validated; /// Error thrown when failed to validate a `Config`. - type ConfigError; + type Error; /// Consumes the merged config, as nothing needs it once it is validated. - fn validate(self) -> Result; + fn validate(self) -> Result; } /// Contains context used for generating validation errors From 34898067351092815586afcb35778dfb74ecda6d Mon Sep 17 00:00:00 2001 From: Maximilian Wittich <56642549+Maleware@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:52:11 +0100 Subject: [PATCH 4/6] reword comment to suggestion Co-authored-by: Techassi --- crates/stackable-operator/src/config/fragment.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index f571f39c5..30d11ae8d 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -27,7 +27,8 @@ pub trait IntoValidated { /// Error thrown when failed to validate a `Config`. type Error; - /// Consumes the merged config, as nothing needs it once it is validated. + /// Consumes and validates self and returns [`Self::Validated`] or [`Self::Error`] if + /// validation failed. fn validate(self) -> Result; } From cc554500529219649d51c2cacd127470cad1a821 Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Fri, 13 Nov 2026 10:53:27 +0100 Subject: [PATCH 5/6] change type to error and change docs --- crates/stackable-operator/src/config/fragment.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index b790e37f3..63c107ef8 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -19,16 +19,16 @@ use super::merge::Merge; #[cfg(doc)] use crate::role_utils::{Role, RoleGroup}; -/// Turns a role group's merged config into the validated config the build step works with. +/// Turns a invalidated struct into the validated counterpart the build step works with. #[allow(dead_code)] pub trait IntoValidated { - /// `ValidatedConfig` of the `Config` to validate. + /// `Validated` counterpart of the struct to validate. type Validated; - /// Error thrown when failed to validate a `Config`. - type ConfigError; + /// `Error` thrown when failed to validate a struct. + type Error; /// Consumes the merged config, as nothing needs it once it is validated. - fn validate(self) -> Result; + fn validate(self) -> Result; } /// Contains context used for generating validation errors From 4807880a662aa00c56b2b10384d8f0404230121f Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Fri, 13 Nov 2026 14:23:40 +0100 Subject: [PATCH 6/6] use valdate() as blanket impl of TryFrom --- .../stackable-operator/src/config/fragment.rs | 13 ----------- .../src/crd/containerdebug/mod.rs | 23 +++++++++---------- crates/stackable-operator/src/validation.rs | 11 +++++++++ 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/crates/stackable-operator/src/config/fragment.rs b/crates/stackable-operator/src/config/fragment.rs index 22142e833..a1a89f6be 100644 --- a/crates/stackable-operator/src/config/fragment.rs +++ b/crates/stackable-operator/src/config/fragment.rs @@ -19,19 +19,6 @@ use super::merge::Merge; #[cfg(doc)] use crate::role_utils::{Role, RoleGroup}; -/// Turns a invalidated struct into the validated counterpart the build step works with. -#[allow(dead_code)] -pub trait IntoValidated { - /// `Validated` counterpart of the struct to validate. - type Validated; - /// Error thrown when failed to validate a `Config`. - type Error; - - /// Consumes and validates self and returns [`Self::Validated`] or [`Self::Error`] if - /// validation failed. - fn validate(self) -> Result; -} - /// Contains context used for generating validation errors /// /// Constructed internally in [`validate`] diff --git a/crates/stackable-operator/src/crd/containerdebug/mod.rs b/crates/stackable-operator/src/crd/containerdebug/mod.rs index 6ed39c1f9..8ea9c3c45 100644 --- a/crates/stackable-operator/src/crd/containerdebug/mod.rs +++ b/crates/stackable-operator/src/crd/containerdebug/mod.rs @@ -12,6 +12,7 @@ use stackable_shared::time::Duration; use crate::{ config::{fragment::Fragment, merge::Merge}, versioned::versioned, + validation::Validate, }; /// The interval used if the user does not configure one. @@ -55,22 +56,20 @@ pub mod versioned { } } -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 { - if !self.enabled { +impl TryFrom for ValidatedContainerdebugConfig{ + type Error = Error; + + fn try_from(value: v1alpha1::ContainerdebugConfig) -> Result { + if !value.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); + ensure!(!value.interval.is_zero(), IntervalZeroSnafu); Ok(ValidatedContainerdebugConfig::Enabled { - interval: self.interval, + interval: value.interval, }) } } @@ -118,6 +117,7 @@ mod tests { use super::*; use crate::config::fragment; + use crate::validation::Validate; const LOG_DIR: &str = "/stackable/log"; @@ -134,8 +134,7 @@ mod tests { fragment::validate(default_containerdebug_config()).expect("defaults are complete"); assert_eq!( - config - .validate() + >::validate(config) .expect("defaults are valid") .background_command(LOG_DIR), Some( @@ -159,7 +158,7 @@ mod tests { /// its role. #[test] fn disabled_ignores_interval_and_renders_nothing() { - let validated = config(false, "0s") + let validated: ValidatedContainerdebugConfig = config(false, "0s") .validate() .expect("the interval is not validated while disabled"); diff --git a/crates/stackable-operator/src/validation.rs b/crates/stackable-operator/src/validation.rs index ef2334bf8..e0898fd28 100644 --- a/crates/stackable-operator/src/validation.rs +++ b/crates/stackable-operator/src/validation.rs @@ -15,6 +15,17 @@ use const_format::concatcp; use regex::Regex; use snafu::Snafu; +pub trait Validate +where + Self: TryInto, +{ + fn validate(self) -> Result { + self.try_into() + } +} + +impl Validate for U where U: TryInto {} + /// Minimal length required by RFC 1123 is 63. Up to 255 allowed, unsupported by k8s. pub const RFC_1123_LABEL_MAX_LENGTH: usize = 63; // This is a modified RFC 1123 format according to the Kubernetes specification, see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names