From 0ef87bc0f8ae1a6100f55dc3285fc480acfe696a Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 7 May 2026 15:49:41 +0200 Subject: [PATCH 1/4] chore!: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides --- crates/stackable-operator/CHANGELOG.md | 6 ++ .../stackable-operator/crds/DummyCluster.yaml | 60 +++++++++++- .../src/config_overrides.rs | 95 ++++++++++++++----- 3 files changed, 133 insertions(+), 28 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 28a1071b3..587b8b66c 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Changed + +- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This changed is marked as breaking, as it causes a breaking change to the CRD ([#XXXX]). + +[#XXXX]: https://github.com/stackabletech/operator-rs/pull/XXXX + ## [0.111.1] - 2026-04-28 ### Added diff --git a/crates/stackable-operator/crds/DummyCluster.yaml b/crates/stackable-operator/crds/DummyCluster.yaml index 3ca133c09..90704393c 100644 --- a/crates/stackable-operator/crds/DummyCluster.yaml +++ b/crates/stackable-operator/crds/DummyCluster.yaml @@ -1007,8 +1007,34 @@ spec: type: string type: array userProvided: - description: Override the entire config file with the specified String. - type: string + description: |- + Override the entire config file with the specified JSON document. + + Please note that you can in-line JSON into YAML as follows: + + ```yaml + # ... other YAML content + userProvided: { + "myString": "test", + "myList": ["test"], + "myBool": true, + "my": {"nested.field.with.dots": 42} + } + ``` + + As an alternative you can also stick to YAML: + + ```yaml + # ... other YAML content + userProvided: + myString: test + myList: [test] + myBool: true + my: + nested.field.with.dots: 42 + ``` + type: object + x-kubernetes-preserve-unknown-fields: true type: object dummy.properties: additionalProperties: @@ -1659,8 +1685,34 @@ spec: type: string type: array userProvided: - description: Override the entire config file with the specified String. - type: string + description: |- + Override the entire config file with the specified JSON document. + + Please note that you can in-line JSON into YAML as follows: + + ```yaml + # ... other YAML content + userProvided: { + "myString": "test", + "myList": ["test"], + "myBool": true, + "my": {"nested.field.with.dots": 42} + } + ``` + + As an alternative you can also stick to YAML: + + ```yaml + # ... other YAML content + userProvided: + myString: test + myList: [test] + myBool: true + my: + nested.field.with.dots: 42 + ``` + type: object + x-kubernetes-preserve-unknown-fields: true type: object dummy.properties: additionalProperties: diff --git a/crates/stackable-operator/src/config_overrides.rs b/crates/stackable-operator/src/config_overrides.rs index e5cc17d93..2b534e758 100644 --- a/crates/stackable-operator/src/config_overrides.rs +++ b/crates/stackable-operator/src/config_overrides.rs @@ -26,9 +26,6 @@ pub enum Error { source: serde_json::Error, index: usize, }, - - #[snafu(display("failed to parse user-provided JSON content"))] - ParseUserProvidedJson { source: serde_json::Error }, } /// Trait that allows the product config pipeline to extract flat key-value @@ -89,8 +86,33 @@ pub enum JsonConfigOverrides { /// `{"op": "add", "path": "/0/happy", "value": true}` JsonPatches(Vec), - /// Override the entire config file with the specified String. - UserProvided(String), + /// Override the entire config file with the specified JSON document. + /// + /// Please note that you can in-line JSON into YAML as follows: + /// + /// ```yaml + /// # ... other YAML content + /// userProvided: { + /// "myString": "test", + /// "myList": ["test"], + /// "myBool": true, + /// "my": {"nested.field.with.dots": 42} + /// } + /// ``` + /// + /// As an alternative you can also stick to YAML: + /// + /// ```yaml + /// # ... other YAML content + /// userProvided: + /// myString: test + /// myList: [test] + /// myBool: true + /// my: + /// nested.field.with.dots: 42 + /// ``` + #[schemars(schema_with = "raw_object_schema")] + UserProvided(serde_json::Value), } impl JsonConfigOverrides { @@ -123,18 +145,18 @@ impl JsonConfigOverrides { json_patch::patch(&mut doc, &operations).context(ApplyJsonPatchSnafu)?; Ok(doc) } - Self::UserProvided(content) => { - serde_json::from_str(content).context(ParseUserProvidedJsonSnafu) - } + Self::UserProvided(content) => Ok(content.clone()), } } } #[cfg(test)] mod tests { + use rstest::rstest; use serde_json::json; use super::*; + use crate::utils::yaml_from_str_singleton_map; #[test] fn json_merge_patch_add_and_overwrite_fields() { @@ -244,9 +266,9 @@ mod tests { #[test] fn user_provided_ignores_base() { let base = json!({"foo": "bar"}); - let content = "{\"custom\": true}"; + let content = json!({"custom": true}); - let overrides = JsonConfigOverrides::UserProvided(content.to_owned()); + let overrides = JsonConfigOverrides::UserProvided(content); let result = overrides .apply(&base) @@ -254,20 +276,6 @@ mod tests { assert_eq!(result, json!({"custom": true})); } - #[test] - fn user_provided_invalid_json_returns_error() { - let base = json!({"foo": "bar"}); - - let overrides = JsonConfigOverrides::UserProvided("not valid json".to_owned()); - - let result = overrides.apply(&base); - assert!( - matches!(result.unwrap_err(), Error::ParseUserProvidedJson { source } if source.to_string() - == "expected ident at line 1 column 2"), - "invalid JSON should return an error" - ); - } - #[test] fn key_value_config_overrides_as_product_config_overrides() { let mut overrides = BTreeMap::new(); @@ -281,4 +289,43 @@ mod tests { assert_eq!(result.get("key1"), Some(&Some("value1".to_owned()))); assert_eq!(result.get("key2"), Some(&Some("value2".to_owned()))); } + + #[rstest] + #[case::inline_json( + r#" +# ... other YAML content +userProvided: { + "myString": "test", + "myList": ["test"], + "myBool": true, + "my": {"nested.field.with.dots": 42} +} +"# + )] + #[case::inline_yaml( + " +# ... other YAML content +userProvided: + myString: test + myList: [test] + myBool: true + my: + nested.field.with.dots: 42 +" + )] + fn parse_user_provided_json(#[case] yaml: String) { + let expected = json!({ + "myString": "test", + "myList": ["test"], + "myBool": true, + "my": {"nested.field.with.dots": 42} + }); + let json_config_overrides: JsonConfigOverrides = + yaml_from_str_singleton_map(&yaml).unwrap(); + + match json_config_overrides { + JsonConfigOverrides::UserProvided(value) => assert_eq!(value, expected), + _ => panic!("JsonConfigOverrides must be of type UserProvided"), + } + } } From 3865bc628f7f6aea471ba4c377e7725ae2c0250d Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 7 May 2026 15:50:57 +0200 Subject: [PATCH 2/4] changelog --- 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 587b8b66c..b558241c0 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -6,9 +6,9 @@ All notable changes to this project will be documented in this file. ### Changed -- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This changed is marked as breaking, as it causes a breaking change to the CRD ([#XXXX]). +- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This changed is marked as breaking, as it causes a breaking change to the CRD ([#1206]). -[#XXXX]: https://github.com/stackabletech/operator-rs/pull/XXXX +[#1206]: https://github.com/stackabletech/operator-rs/pull/1206 ## [0.111.1] - 2026-04-28 From d8d099b12e5c1352b5eeec22acdec00984d5ded4 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 8 May 2026 09:00:45 +0200 Subject: [PATCH 3/4] Update crates/stackable-operator/CHANGELOG.md Co-authored-by: Lukas Krug --- crates/stackable-operator/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index b558241c0..b759555af 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. ### Changed -- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This changed is marked as breaking, as it causes a breaking change to the CRD ([#1206]). +- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This change is marked as breaking, as it causes a breaking change to the CRD ([#1206]). [#1206]: https://github.com/stackabletech/operator-rs/pull/1206 From ff260658ec4d23c1a5017898ec698efb23f2a522 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 8 May 2026 09:01:22 +0200 Subject: [PATCH 4/4] Update crates/stackable-operator/CHANGELOG.md --- crates/stackable-operator/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index b759555af..676de476f 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. ### Changed -- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON configOverrides. This change is marked as breaking, as it causes a breaking change to the CRD ([#1206]). +- BREAKING: Use `serde_json::Value` instead of `String` for user-provided JSON `configOverrides`. This change is marked as breaking, as it causes a breaking change to the CRDs ([#1206]). [#1206]: https://github.com/stackabletech/operator-rs/pull/1206