From 267de2999ecc047ea39ec2bc4d73c4e556a7e4e1 Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Tue, 7 Jul 2026 14:06:43 +0200 Subject: [PATCH 1/3] fix: Fix the JSON schema of JsonConfigOverrides --- crates/stackable-operator/CHANGELOG.md | 2 + .../src/v2/config_overrides.rs | 79 ++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 7f54726ef..9036f8554 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -7,8 +7,10 @@ All notable changes to this project will be documented in this file. ### Fixed - [v2] Don't require the `attributed_string_type` macro caller to have `std::str::FromStr` in scope ([#1244]). +- [v2] Fix the JSON schema of `JsonConfigOverrides` ([#1245]). [#1244]: https://github.com/stackabletech/operator-rs/pull/1244 +[#1245]: https://github.com/stackabletech/operator-rs/pull/1245 ## [0.113.2] - 2026-07-07 diff --git a/crates/stackable-operator/src/v2/config_overrides.rs b/crates/stackable-operator/src/v2/config_overrides.rs index 2b404ea99..e3bb1c269 100644 --- a/crates/stackable-operator/src/v2/config_overrides.rs +++ b/crates/stackable-operator/src/v2/config_overrides.rs @@ -9,7 +9,10 @@ use serde_json::json; use tracing::warn; use crate::{ - config::merge::Merge, k8s_openapi::DeepMerge, schemars, utils::crds::raw_object_schema, + config::merge::Merge, + k8s_openapi::DeepMerge, + schemars, + utils::crds::{raw_object_list_schema, raw_object_schema}, }; // Variant of [`crate::config_overrides::KeyValueConfigOverrides`] that implements @@ -93,7 +96,7 @@ pub enum JsonConfigOverrides { /// or /// /// `- {"op": "add", "path": "/0/happy", "value": true}` - #[schemars(schema_with = "raw_object_schema")] + #[schemars(schema_with = "raw_object_list_schema")] JsonPatch(json_patch::Patch), /// Override the entire config file with the specified JSON value. @@ -251,11 +254,83 @@ impl Merge for JsonOrKeyValueConfigOverrides { #[cfg(test)] mod tests { + use indoc::indoc; + use kube::core::schema::StructuralSchemaRewriter; + use schemars::generate::SchemaSettings; use serde_json::json; use super::*; use crate::config::merge; + #[test] + fn test_json_config_overrides_schema() { + let schema = SchemaSettings::default() + .with_transform(StructuralSchemaRewriter) + .into_generator() + .into_root_schema_for::(); + + assert_eq!( + json!({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "ConfigOverrides that can be applied to a JSON file.", + "oneOf": [ + { + "required": [ + "jsonMergePatch" + ] + }, + { + "required": [ + "jsonPatch" + ] + }, + { + "required": [ + "userProvided" + ] + } + ], + "properties": { + "jsonMergePatch": { + "description": indoc!(" + Can be set to arbitrary YAML content, which is converted to JSON and used as + [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) JSON merge patch."), + "type": "object", + "x-kubernetes-preserve-unknown-fields": true + }, + "jsonPatch": { + "description": indoc!(r#" + An [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patch. + + Can be used when more flexibility is needed, e.g. to only modify elements + in a list based on a condition. + + A patch looks something like + + `- {"op": "test", "path": "/0/name", "value": "Andrew"}` + + or + + `- {"op": "add", "path": "/0/happy", "value": true}`"#), + "items": { + "type": "object", + "x-kubernetes-preserve-unknown-fields": true + }, + "type": "array" + }, + "userProvided": { + "description": "Override the entire config file with the specified JSON value.", + "type": "object", + "x-kubernetes-preserve-unknown-fields": true + } + }, + "title": "JsonConfigOverrides", + "type": "object" + }), + schema + ); + } + #[test] fn test_json_config_overrides_apply() { let base = json!({ From 8d7f40f4f6ac0420e6df24d718bccafe7f336f7e Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Tue, 7 Jul 2026 15:08:59 +0200 Subject: [PATCH 2/3] test: Test JsonConfigOverrides in the DummyCluster instead of a unit test --- .../stackable-operator/crds/DummyCluster.yaml | 80 ++++--------------- .../src/v2/config_overrides.rs | 72 ----------------- crates/xtask/src/crd/dummy.rs | 8 +- 3 files changed, 22 insertions(+), 138 deletions(-) diff --git a/crates/stackable-operator/crds/DummyCluster.yaml b/crates/stackable-operator/crds/DummyCluster.yaml index 79692113e..01b017b2a 100644 --- a/crates/stackable-operator/crds/DummyCluster.yaml +++ b/crates/stackable-operator/crds/DummyCluster.yaml @@ -979,7 +979,7 @@ spec: - required: - jsonMergePatch - required: - - jsonPatches + - jsonPatch - required: - userProvided properties: @@ -989,50 +989,26 @@ spec: [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) JSON merge patch. type: object x-kubernetes-preserve-unknown-fields: true - jsonPatches: + jsonPatch: description: |- - List of [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patches. + An [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patch. Can be used when more flexibility is needed, e.g. to only modify elements in a list based on a condition. A patch looks something like - `{"op": "test", "path": "/0/name", "value": "Andrew"}` + `- {"op": "test", "path": "/0/name", "value": "Andrew"}` or - `{"op": "add", "path": "/0/happy", "value": true}` + `- {"op": "add", "path": "/0/happy", "value": true}` items: - type: string + type: object + x-kubernetes-preserve-unknown-fields: true type: array userProvided: - 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 - ``` + description: Override the entire config file with the specified JSON value. type: object x-kubernetes-preserve-unknown-fields: true type: object @@ -1657,7 +1633,7 @@ spec: - required: - jsonMergePatch - required: - - jsonPatches + - jsonPatch - required: - userProvided properties: @@ -1667,50 +1643,26 @@ spec: [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) JSON merge patch. type: object x-kubernetes-preserve-unknown-fields: true - jsonPatches: + jsonPatch: description: |- - List of [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patches. + An [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patch. Can be used when more flexibility is needed, e.g. to only modify elements in a list based on a condition. A patch looks something like - `{"op": "test", "path": "/0/name", "value": "Andrew"}` + `- {"op": "test", "path": "/0/name", "value": "Andrew"}` or - `{"op": "add", "path": "/0/happy", "value": true}` + `- {"op": "add", "path": "/0/happy", "value": true}` items: - type: string + type: object + x-kubernetes-preserve-unknown-fields: true type: array userProvided: - 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 - ``` + description: Override the entire config file with the specified JSON value. type: object x-kubernetes-preserve-unknown-fields: true type: object diff --git a/crates/stackable-operator/src/v2/config_overrides.rs b/crates/stackable-operator/src/v2/config_overrides.rs index e3bb1c269..559b5bd5d 100644 --- a/crates/stackable-operator/src/v2/config_overrides.rs +++ b/crates/stackable-operator/src/v2/config_overrides.rs @@ -254,83 +254,11 @@ impl Merge for JsonOrKeyValueConfigOverrides { #[cfg(test)] mod tests { - use indoc::indoc; - use kube::core::schema::StructuralSchemaRewriter; - use schemars::generate::SchemaSettings; use serde_json::json; use super::*; use crate::config::merge; - #[test] - fn test_json_config_overrides_schema() { - let schema = SchemaSettings::default() - .with_transform(StructuralSchemaRewriter) - .into_generator() - .into_root_schema_for::(); - - assert_eq!( - json!({ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "ConfigOverrides that can be applied to a JSON file.", - "oneOf": [ - { - "required": [ - "jsonMergePatch" - ] - }, - { - "required": [ - "jsonPatch" - ] - }, - { - "required": [ - "userProvided" - ] - } - ], - "properties": { - "jsonMergePatch": { - "description": indoc!(" - Can be set to arbitrary YAML content, which is converted to JSON and used as - [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) JSON merge patch."), - "type": "object", - "x-kubernetes-preserve-unknown-fields": true - }, - "jsonPatch": { - "description": indoc!(r#" - An [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patch. - - Can be used when more flexibility is needed, e.g. to only modify elements - in a list based on a condition. - - A patch looks something like - - `- {"op": "test", "path": "/0/name", "value": "Andrew"}` - - or - - `- {"op": "add", "path": "/0/happy", "value": true}`"#), - "items": { - "type": "object", - "x-kubernetes-preserve-unknown-fields": true - }, - "type": "array" - }, - "userProvided": { - "description": "Override the entire config file with the specified JSON value.", - "type": "object", - "x-kubernetes-preserve-unknown-fields": true - } - }, - "title": "JsonConfigOverrides", - "type": "object" - }), - schema - ); - } - #[test] fn test_json_config_overrides_apply() { let base = json!({ diff --git a/crates/xtask/src/crd/dummy.rs b/crates/xtask/src/crd/dummy.rs index b1e070768..67cbbfe11 100644 --- a/crates/xtask/src/crd/dummy.rs +++ b/crates/xtask/src/crd/dummy.rs @@ -4,8 +4,11 @@ use serde::{Deserialize, Serialize}; use stackable_operator::{ commons::resources::{JvmHeapLimits, Resources}, config::fragment::Fragment, - config_overrides::{JsonConfigOverrides, KeyValueConfigOverrides, KeyValueOverridesProvider}, - crd::{authentication, authentication::oidc, git_sync::v1alpha2::GitSync}, + config_overrides::{KeyValueConfigOverrides, KeyValueOverridesProvider}, + crd::{ + authentication::{self, oidc}, + git_sync::v1alpha2::GitSync, + }, database_connections::{ databases::{ derby::DerbyConnection, mysql::MysqlConnection, postgresql::PostgresqlConnection, @@ -21,6 +24,7 @@ use stackable_operator::{ role_utils::Role, schemars::JsonSchema, status::condition::ClusterCondition, + v2::config_overrides::JsonConfigOverrides, versioned::versioned, }; use strum::EnumIter; From 3b9832327df59233f5006de843579ee8ed63bc9c Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Tue, 7 Jul 2026 15:24:04 +0200 Subject: [PATCH 3/3] Extend the documentation of the userProvided JsonConfigOverrides --- .../stackable-operator/crds/DummyCluster.yaml | 54 ++++++++++++++++++- .../src/v2/config_overrides.rs | 26 ++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/crds/DummyCluster.yaml b/crates/stackable-operator/crds/DummyCluster.yaml index 01b017b2a..ac014b438 100644 --- a/crates/stackable-operator/crds/DummyCluster.yaml +++ b/crates/stackable-operator/crds/DummyCluster.yaml @@ -1008,7 +1008,32 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array userProvided: - description: Override the entire config file with the specified JSON value. + 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 @@ -1662,7 +1687,32 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array userProvided: - description: Override the entire config file with the specified JSON value. + 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 diff --git a/crates/stackable-operator/src/v2/config_overrides.rs b/crates/stackable-operator/src/v2/config_overrides.rs index 559b5bd5d..ff40cf68d 100644 --- a/crates/stackable-operator/src/v2/config_overrides.rs +++ b/crates/stackable-operator/src/v2/config_overrides.rs @@ -99,7 +99,31 @@ pub enum JsonConfigOverrides { #[schemars(schema_with = "raw_object_list_schema")] JsonPatch(json_patch::Patch), - /// Override the entire config file with the specified JSON value. + /// 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),