From 41e88ff7eef86b568354442d8eb366324d97d960 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Mon, 11 Aug 2025 17:26:25 +0200 Subject: [PATCH 01/10] fix invalid app version label when using hash --- .../src/commons/product_image_selection.rs | 89 +++++++++++++------ .../src/crd/git_sync/v1alpha1_impl.rs | 8 +- .../stackable-operator/src/kvp/label/value.rs | 5 +- .../src/product_logging/framework.rs | 2 +- 4 files changed, 70 insertions(+), 34 deletions(-) diff --git a/crates/stackable-operator/src/commons/product_image_selection.rs b/crates/stackable-operator/src/commons/product_image_selection.rs index 6fe5ba674..74156e54f 100644 --- a/crates/stackable-operator/src/commons/product_image_selection.rs +++ b/crates/stackable-operator/src/commons/product_image_selection.rs @@ -2,13 +2,22 @@ use dockerfile_parser::ImageRef; use k8s_openapi::api::core::v1::LocalObjectReference; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use snafu::{ResultExt, Snafu}; use strum::AsRefStr; -#[cfg(doc)] -use crate::kvp::Labels; +use crate::kvp::{LABEL_VALUE_MAX_LEN, LabelValue, LabelValueError}; pub const STACKABLE_DOCKER_REPO: &str = "oci.stackable.tech/sdp"; +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("could not parse or create label from app version `{app_version}`"))] + AppVersionLabelParseFailed { + source: LabelValueError, + app_version: String, + }, +} + /// Specify which image to use, the easiest way is to only configure the `productVersion`. /// You can also configure a custom image registry to pull from, as well as completely custom /// images. @@ -67,8 +76,8 @@ pub struct ResolvedProductImage { /// Version of the product, e.g. `1.4.1`. pub product_version: String, - /// App version as formatted for [`Labels::recommended`] - pub app_version_label: String, + /// App version formatted for Labels + pub app_version_label: LabelValue, /// Image to be used for the product image e.g. `oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0` pub image: String, @@ -101,7 +110,11 @@ impl ProductImage { /// `image_base_name` should be base of the image name in the container image registry, e.g. `trino`. /// `operator_version` needs to be the full operator version and a valid semver string. /// Accepted values are `23.7.0`, `0.0.0-dev` or `0.0.0-pr123`. Other variants are not supported. - pub fn resolve(&self, image_base_name: &str, operator_version: &str) -> ResolvedProductImage { + pub fn resolve( + &self, + image_base_name: &str, + operator_version: &str, + ) -> Result { let image_pull_policy = self.pull_policy.as_ref().to_string(); let pull_secrets = self.pull_secrets.clone(); @@ -111,17 +124,17 @@ impl ProductImage { ProductImageSelection::Custom(image_selection) => { let image = ImageRef::parse(&image_selection.custom); let image_tag_or_hash = image.tag.or(image.hash).unwrap_or("latest".to_string()); - let mut app_version_label = format!("{}-{}", product_version, image_tag_or_hash); - // TODO Use new label mechanism once added - app_version_label.truncate(63); - ResolvedProductImage { + let app_version = format!("{}-{}", product_version, image_tag_or_hash); + let app_version_label = Self::prepare_app_version_label(&app_version)?; + + Ok(ResolvedProductImage { product_version, app_version_label, image: image_selection.custom.clone(), image_pull_policy, pull_secrets, - } + }) } ProductImageSelection::StackableVersion(image_selection) => { let repo = image_selection @@ -147,14 +160,15 @@ impl ProductImage { let image = format!( "{repo}/{image_base_name}:{product_version}-stackable{stackable_version}", ); - let app_version_label = format!("{product_version}-stackable{stackable_version}",); - ResolvedProductImage { + let app_version = format!("{product_version}-stackable{stackable_version}"); + let app_version_label = Self::prepare_app_version_label(&app_version)?; + Ok(ResolvedProductImage { product_version, app_version_label, image, image_pull_policy, pull_secrets, - } + }) } } } @@ -174,6 +188,21 @@ impl ProductImage { }) => pv, } } + + fn prepare_app_version_label(app_version: &str) -> Result { + let mut formatted_app_version = app_version.to_string(); + // Labels cannot have more than `LABEL_VALUE_MAX_LEN` characters. + formatted_app_version.truncate(LABEL_VALUE_MAX_LEN); + // The hash has the format `sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb` + // As the colon (`:`) is not a valid label value character, we replace it with a valid "-" character. + let formatted_app_version = formatted_app_version.replace(":", "-"); + + formatted_app_version + .parse() + .with_context(|_| AppVersionLabelParseFailedSnafu { + app_version: formatted_app_version.to_string(), + }) + } } #[cfg(test)] @@ -191,7 +220,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable23.7.42".to_string(), - app_version_label: "1.4.1-stackable23.7.42".to_string(), + app_version_label: "1.4.1-stackable23.7.42".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -205,7 +234,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev".to_string(), - app_version_label: "1.4.1-stackable0.0.0-dev".to_string(), + app_version_label: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -219,7 +248,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev".to_string(), - app_version_label: "1.4.1-stackable0.0.0-dev".to_string(), + app_version_label: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -234,7 +263,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0".to_string(), - app_version_label: "1.4.1-stackable2.1.0".to_string(), + app_version_label: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -250,7 +279,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/trino:1.4.1-stackable2.1.0".to_string(), - app_version_label: "1.4.1-stackable2.1.0".to_string(), + app_version_label: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -265,7 +294,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset".to_string(), - app_version_label: "1.4.1-latest".to_string(), + app_version_label: "1.4.1-latest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -280,7 +309,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -295,7 +324,7 @@ mod tests { "#, ResolvedProductImage { image: "127.0.0.1:8080/myteam/stackable/superset".to_string(), - app_version_label: "1.4.1-latest".to_string(), + app_version_label: "1.4.1-latest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -310,7 +339,7 @@ mod tests { "#, ResolvedProductImage { image: "127.0.0.1:8080/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -325,7 +354,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset@sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb8c42f76efc1098".to_string(), - app_version_label: "1.4.1-sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb".to_string(), + app_version_label: "1.4.1-sha256-85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -341,7 +370,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -357,7 +386,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "IfNotPresent".to_string(), pull_secrets: None, @@ -373,7 +402,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -389,7 +418,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Never".to_string(), pull_secrets: None, @@ -408,7 +437,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".to_string(), + app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: Some(vec![LocalObjectReference{name: "myPullSecrets1".to_string()}, LocalObjectReference{name: "myPullSecrets2".to_string()}]), @@ -421,7 +450,9 @@ mod tests { #[case] expected: ResolvedProductImage, ) { let product_image: ProductImage = serde_yaml::from_str(&input).expect("Illegal test input"); - let resolved_product_image = product_image.resolve(&image_base_name, &operator_version); + let resolved_product_image = product_image + .resolve(&image_base_name, &operator_version) + .expect("Illegal test input"); assert_eq!(resolved_product_image, expected); } diff --git a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs index 8193123ec..2c29156c4 100644 --- a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs +++ b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs @@ -374,7 +374,9 @@ mod tests { let resolved_product_image = ResolvedProductImage { image: "oci.stackable.tech/sdp/product:latest".to_string(), - app_version_label: "1.0.0-latest".to_string(), + app_version_label: "1.0.0-latest" + .parse() + .expect("static app version label is always valid"), product_version: "1.0.0".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -439,7 +441,9 @@ mod tests { let resolved_product_image = ResolvedProductImage { image: "oci.stackable.tech/sdp/product:latest".to_string(), - app_version_label: "1.0.0-latest".to_string(), + app_version_label: "1.0.0-latest" + .parse() + .expect("static app version label is always valid"), product_version: "1.0.0".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, diff --git a/crates/stackable-operator/src/kvp/label/value.rs b/crates/stackable-operator/src/kvp/label/value.rs index 7135ceef7..510887e65 100644 --- a/crates/stackable-operator/src/kvp/label/value.rs +++ b/crates/stackable-operator/src/kvp/label/value.rs @@ -1,11 +1,12 @@ use std::{fmt::Display, ops::Deref, str::FromStr, sync::LazyLock}; use regex::Regex; +use schemars::JsonSchema; use snafu::{Snafu, ensure}; use crate::kvp::Value; -const LABEL_VALUE_MAX_LEN: usize = 63; +pub const LABEL_VALUE_MAX_LEN: usize = 63; // Lazily initialized regular expressions static LABEL_VALUE_REGEX: LazyLock = LazyLock::new(|| { @@ -43,7 +44,7 @@ pub enum LabelValueError { /// unvalidated mutable access to inner values. /// /// [k8s-labels]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub struct LabelValue(String); impl Value for LabelValue { diff --git a/crates/stackable-operator/src/product_logging/framework.rs b/crates/stackable-operator/src/product_logging/framework.rs index d62a6445c..907f1f3bc 100644 --- a/crates/stackable-operator/src/product_logging/framework.rs +++ b/crates/stackable-operator/src/product_logging/framework.rs @@ -1382,7 +1382,7 @@ sinks: /// /// # let resolved_product_image = ResolvedProductImage { /// # product_version: "1.0.0".into(), -/// # app_version_label: "1.0.0".into(), +/// # app_version_label: "1.0.0".parse().expect("static app version label is always valid"), /// # image: "oci.stackable.tech/sdp/my-product:1.0.0-stackable1.0.0".into(), /// # image_pull_policy: "Always".into(), /// # pull_secrets: None, From 8bfb046385f72b262a96727928feb050ad127782 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Mon, 11 Aug 2025 17:34:01 +0200 Subject: [PATCH 02/10] adapted changelog --- crates/stackable-operator/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index a8d66b5ee..2fcb39e75 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed + +- BREAKING: Fixed bug where `app_version_label` could not be used for metadata `Label` when using a hash in custom images. + The `product_image_selection::resolve` now returns a `Result` instead of a `ResolvedProductImage` ([#1076]). + +[#1076]: https://github.com/stackabletech/operator-rs/pull/1076 + ## [0.94.0] - 2025-07-10 ### Added From 30975ad71e4fd911907334f985f06028050eb418 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 09:47:31 +0200 Subject: [PATCH 03/10] Apply suggestions from code review Co-authored-by: Sebastian Bernauer --- crates/stackable-operator/CHANGELOG.md | 2 +- .../stackable-operator/src/commons/product_image_selection.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 2fcb39e75..46512e290 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. ### Fixed - BREAKING: Fixed bug where `app_version_label` could not be used for metadata `Label` when using a hash in custom images. - The `product_image_selection::resolve` now returns a `Result` instead of a `ResolvedProductImage` ([#1076]). + The `product_image_selection::resolve` now returns a `Result` instead of a `ResolvedProductImage` ([#1076]). [#1076]: https://github.com/stackabletech/operator-rs/pull/1076 diff --git a/crates/stackable-operator/src/commons/product_image_selection.rs b/crates/stackable-operator/src/commons/product_image_selection.rs index 74156e54f..d155ddfa4 100644 --- a/crates/stackable-operator/src/commons/product_image_selection.rs +++ b/crates/stackable-operator/src/commons/product_image_selection.rs @@ -11,7 +11,7 @@ pub const STACKABLE_DOCKER_REPO: &str = "oci.stackable.tech/sdp"; #[derive(Debug, Snafu)] pub enum Error { - #[snafu(display("could not parse or create label from app version `{app_version}`"))] + #[snafu(display("could not parse or create label from app version {app_version:?}"))] AppVersionLabelParseFailed { source: LabelValueError, app_version: String, From c69e5deddb0f04f513e48245c7f28ed61346668c Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 09:48:39 +0200 Subject: [PATCH 04/10] rename error --- .../stackable-operator/src/commons/product_image_selection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/src/commons/product_image_selection.rs b/crates/stackable-operator/src/commons/product_image_selection.rs index d155ddfa4..d3f677662 100644 --- a/crates/stackable-operator/src/commons/product_image_selection.rs +++ b/crates/stackable-operator/src/commons/product_image_selection.rs @@ -12,7 +12,7 @@ pub const STACKABLE_DOCKER_REPO: &str = "oci.stackable.tech/sdp"; #[derive(Debug, Snafu)] pub enum Error { #[snafu(display("could not parse or create label from app version {app_version:?}"))] - AppVersionLabelParseFailed { + ParseAppVersionLabel { source: LabelValueError, app_version: String, }, @@ -199,7 +199,7 @@ impl ProductImage { formatted_app_version .parse() - .with_context(|_| AppVersionLabelParseFailedSnafu { + .with_context(|_| ParseAppVersionLabelSnafu { app_version: formatted_app_version.to_string(), }) } From e38c0b8af642c240201163d529a35a8b4228b078 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 13:51:56 +0200 Subject: [PATCH 05/10] Update crates/stackable-operator/CHANGELOG.md Co-authored-by: Techassi --- crates/stackable-operator/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 46512e290..4f0f44129 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -6,8 +6,8 @@ All notable changes to this project will be documented in this file. ### Fixed -- BREAKING: Fixed bug where `app_version_label` could not be used for metadata `Label` when using a hash in custom images. - The `product_image_selection::resolve` now returns a `Result` instead of a `ResolvedProductImage` ([#1076]). +- BREAKING: Fix bug where `ResolvedProductImage::app_version_label` could not be used as a label value because it can contain invalid characters. + This is the case when referencing custom images via a `@sha256:...` hash. As such, the `product_image_selection::resolve` function is now fallible. [#1076]: https://github.com/stackabletech/operator-rs/pull/1076 From 8c8c3acaad47aa251af8c9955d4a18fbaa37d615 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 13:56:58 +0200 Subject: [PATCH 06/10] rename app_version_label to app_version_label_value --- crates/stackable-operator/CHANGELOG.md | 1 + .../src/commons/product_image_selection.rs | 42 +++++++++---------- .../src/crd/git_sync/v1alpha1_impl.rs | 4 +- .../src/product_logging/framework.rs | 2 +- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 4f0f44129..eff22f05c 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - BREAKING: Fix bug where `ResolvedProductImage::app_version_label` could not be used as a label value because it can contain invalid characters. This is the case when referencing custom images via a `@sha256:...` hash. As such, the `product_image_selection::resolve` function is now fallible. + The `ResolvedProductImage` field `app_version_label` was renamed to `app_version_label_value` to match its type ([#1076]). [#1076]: https://github.com/stackabletech/operator-rs/pull/1076 diff --git a/crates/stackable-operator/src/commons/product_image_selection.rs b/crates/stackable-operator/src/commons/product_image_selection.rs index d3f677662..61704b10d 100644 --- a/crates/stackable-operator/src/commons/product_image_selection.rs +++ b/crates/stackable-operator/src/commons/product_image_selection.rs @@ -77,7 +77,7 @@ pub struct ResolvedProductImage { pub product_version: String, /// App version formatted for Labels - pub app_version_label: LabelValue, + pub app_version_label_value: LabelValue, /// Image to be used for the product image e.g. `oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0` pub image: String, @@ -126,11 +126,11 @@ impl ProductImage { let image_tag_or_hash = image.tag.or(image.hash).unwrap_or("latest".to_string()); let app_version = format!("{}-{}", product_version, image_tag_or_hash); - let app_version_label = Self::prepare_app_version_label(&app_version)?; + let app_version_label_value = Self::prepare_app_version_label_value(&app_version)?; Ok(ResolvedProductImage { product_version, - app_version_label, + app_version_label_value, image: image_selection.custom.clone(), image_pull_policy, pull_secrets, @@ -161,10 +161,10 @@ impl ProductImage { "{repo}/{image_base_name}:{product_version}-stackable{stackable_version}", ); let app_version = format!("{product_version}-stackable{stackable_version}"); - let app_version_label = Self::prepare_app_version_label(&app_version)?; + let app_version_label_value = Self::prepare_app_version_label_value(&app_version)?; Ok(ResolvedProductImage { product_version, - app_version_label, + app_version_label_value, image, image_pull_policy, pull_secrets, @@ -189,7 +189,7 @@ impl ProductImage { } } - fn prepare_app_version_label(app_version: &str) -> Result { + fn prepare_app_version_label_value(app_version: &str) -> Result { let mut formatted_app_version = app_version.to_string(); // Labels cannot have more than `LABEL_VALUE_MAX_LEN` characters. formatted_app_version.truncate(LABEL_VALUE_MAX_LEN); @@ -220,7 +220,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable23.7.42".to_string(), - app_version_label: "1.4.1-stackable23.7.42".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-stackable23.7.42".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -234,7 +234,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev".to_string(), - app_version_label: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -248,7 +248,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev".to_string(), - app_version_label: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-stackable0.0.0-dev".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -263,7 +263,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0".to_string(), - app_version_label: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -279,7 +279,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/trino:1.4.1-stackable2.1.0".to_string(), - app_version_label: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-stackable2.1.0".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -294,7 +294,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset".to_string(), - app_version_label: "1.4.1-latest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -309,7 +309,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -324,7 +324,7 @@ mod tests { "#, ResolvedProductImage { image: "127.0.0.1:8080/myteam/stackable/superset".to_string(), - app_version_label: "1.4.1-latest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -339,7 +339,7 @@ mod tests { "#, ResolvedProductImage { image: "127.0.0.1:8080/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -354,7 +354,7 @@ mod tests { "#, ResolvedProductImage { image: "oci.stackable.tech/sdp/superset@sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb8c42f76efc1098".to_string(), - app_version_label: "1.4.1-sha256-85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-sha256-85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -370,7 +370,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -386,7 +386,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "IfNotPresent".to_string(), pull_secrets: None, @@ -402,7 +402,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: None, @@ -418,7 +418,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Never".to_string(), pull_secrets: None, @@ -437,7 +437,7 @@ mod tests { "#, ResolvedProductImage { image: "my.corp/myteam/stackable/superset:latest-and-greatest".to_string(), - app_version_label: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), + app_version_label_value: "1.4.1-latest-and-greatest".parse().expect("static app version label is always valid"), product_version: "1.4.1".to_string(), image_pull_policy: "Always".to_string(), pull_secrets: Some(vec![LocalObjectReference{name: "myPullSecrets1".to_string()}, LocalObjectReference{name: "myPullSecrets2".to_string()}]), diff --git a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs index 2c29156c4..934204dc7 100644 --- a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs +++ b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs @@ -374,7 +374,7 @@ mod tests { let resolved_product_image = ResolvedProductImage { image: "oci.stackable.tech/sdp/product:latest".to_string(), - app_version_label: "1.0.0-latest" + app_version_label_value: "1.0.0-latest" .parse() .expect("static app version label is always valid"), product_version: "1.0.0".to_string(), @@ -441,7 +441,7 @@ mod tests { let resolved_product_image = ResolvedProductImage { image: "oci.stackable.tech/sdp/product:latest".to_string(), - app_version_label: "1.0.0-latest" + app_version_label_value: "1.0.0-latest" .parse() .expect("static app version label is always valid"), product_version: "1.0.0".to_string(), diff --git a/crates/stackable-operator/src/product_logging/framework.rs b/crates/stackable-operator/src/product_logging/framework.rs index 907f1f3bc..0279a23a8 100644 --- a/crates/stackable-operator/src/product_logging/framework.rs +++ b/crates/stackable-operator/src/product_logging/framework.rs @@ -1382,7 +1382,7 @@ sinks: /// /// # let resolved_product_image = ResolvedProductImage { /// # product_version: "1.0.0".into(), -/// # app_version_label: "1.0.0".parse().expect("static app version label is always valid"), +/// # app_version_label_value: "1.0.0".parse().expect("static app version label is always valid"), /// # image: "oci.stackable.tech/sdp/my-product:1.0.0-stackable1.0.0".into(), /// # image_pull_policy: "Always".into(), /// # pull_secrets: None, From 3e66e099e0c6adee6acff7e3b167a0d55db319ab Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 14:15:15 +0200 Subject: [PATCH 07/10] fix doc linter --- crates/stackable-operator/src/kvp/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-operator/src/kvp/mod.rs b/crates/stackable-operator/src/kvp/mod.rs index f5aab1b83..8aaee66be 100644 --- a/crates/stackable-operator/src/kvp/mod.rs +++ b/crates/stackable-operator/src/kvp/mod.rs @@ -380,7 +380,7 @@ pub struct ObjectLabels<'a, T> { /// /// However, this is pure documentation and should not be parsed. /// - /// [avl]: crate::commons::product_image_selection::ResolvedProductImage::app_version_label + /// [avl]: crate::commons::product_image_selection::ResolvedProductImage::app_version_label_value pub app_version: &'a str, /// The DNS-style name of the operator managing the object (such as `zookeeper.stackable.tech`) From c6357c55e8a0dbf6bcb6ae2b1332dbcdb2379e09 Mon Sep 17 00:00:00 2001 From: Techassi Date: Tue, 12 Aug 2025 15:23:14 +0200 Subject: [PATCH 08/10] fix: Bump slab to 0.4.11 to fix RUSTSEC-2025-0047 Also see https://rustsec.org/advisories/RUSTSEC-2025-0047 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e909feef..71ad9c779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2861,9 +2861,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" From e4f1f881dcbf26631014fa47e1466a81a9b9303e Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 15:43:57 +0200 Subject: [PATCH 09/10] move field name change under changed section --- crates/stackable-operator/CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index eff22f05c..1142cc823 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,11 +4,15 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Changed + +- BREAKING: The `ResolvedProductImage` field `app_version_label` was renamed to `app_version_label_value` to match changes to its type ([#1076]). + ### Fixed - BREAKING: Fix bug where `ResolvedProductImage::app_version_label` could not be used as a label value because it can contain invalid characters. - This is the case when referencing custom images via a `@sha256:...` hash. As such, the `product_image_selection::resolve` function is now fallible. - The `ResolvedProductImage` field `app_version_label` was renamed to `app_version_label_value` to match its type ([#1076]). + This is the case when referencing custom images via a `@sha256:...` hash. As such, the `product_image_selection::resolve` function is now fallible ([#1076]). + [#1076]: https://github.com/stackabletech/operator-rs/pull/1076 From c258b5ad679d43480c39e7d2d936103221106297 Mon Sep 17 00:00:00 2001 From: Malte Sander Date: Tue, 12 Aug 2025 16:04:10 +0200 Subject: [PATCH 10/10] linter --- crates/stackable-operator/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 1142cc823..86945a87f 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -13,7 +13,6 @@ All notable changes to this project will be documented in this file. - BREAKING: Fix bug where `ResolvedProductImage::app_version_label` could not be used as a label value because it can contain invalid characters. This is the case when referencing custom images via a `@sha256:...` hash. As such, the `product_image_selection::resolve` function is now fallible ([#1076]). - [#1076]: https://github.com/stackabletech/operator-rs/pull/1076 ## [0.94.0] - 2025-07-10