Skip to content

Commit 4abc6a4

Browse files
feat: Support jsonMergePatch in configOverrides
1 parent fb55831 commit 4abc6a4

14 files changed

Lines changed: 414 additions & 165 deletions

File tree

Cargo.lock

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.nix

Lines changed: 11 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git",
1515
built = { version = "0.8.0", features = ["chrono", "git2"] }
1616
clap = "4.5"
1717
futures = { version = "0.3", features = ["compat"] }
18+
json-patch = "4.2"
1819
pretty_assertions = "1.4"
1920
regex = "1.11"
2021
rstest = "0.26"
2122
serde = { version = "1.0", features = ["derive"] }
2223
serde_json = "1.0"
24+
serde_yaml = "0.9"
2325
snafu = { version = "0.9", features = ["futures"] }
2426
strum = { version = "0.28", features = ["derive"] }
2527
tokio = { version = "1.47", features = ["full"] }

extra/crds.yaml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,16 +1867,10 @@ spec:
18671867
available config files and settings for the specific product.
18681868
properties:
18691869
opensearch.yml:
1870-
additionalProperties:
1871-
nullable: true
1872-
type: string
1873-
default: {}
1874-
description: |-
1875-
Flat key-value overrides for `*.properties`, Hadoop XML, etc.
1876-
1877-
This is backwards-compatible with the existing flat key-value YAML format
1878-
used by `HashMap<String, String>`.
1870+
default:
1871+
jsonMergePatch: {}
18791872
type: object
1873+
x-kubernetes-preserve-unknown-fields: true
18801874
type: object
18811875
envOverrides:
18821876
additionalProperties:
@@ -2563,16 +2557,10 @@ spec:
25632557
available config files and settings for the specific product.
25642558
properties:
25652559
opensearch.yml:
2566-
additionalProperties:
2567-
nullable: true
2568-
type: string
2569-
default: {}
2570-
description: |-
2571-
Flat key-value overrides for `*.properties`, Hadoop XML, etc.
2572-
2573-
This is backwards-compatible with the existing flat key-value YAML format
2574-
used by `HashMap<String, String>`.
2560+
default:
2561+
jsonMergePatch: {}
25752562
type: object
2563+
x-kubernetes-preserve-unknown-fields: true
25762564
type: object
25772565
envOverrides:
25782566
additionalProperties:

rust/operator-binary/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ stackable-operator.workspace = true
1414

1515
clap.workspace = true
1616
futures.workspace = true
17+
json-patch.workspace = true
1718
regex.workspace = true
1819
serde.workspace = true
1920
serde_json.workspace = true
21+
serde_yaml.workspace = true
2022
snafu.workspace = true
2123
strum.workspace = true
2224
tokio.workspace = true

rust/operator-binary/src/controller/build/node_config.rs

Lines changed: 39 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! Configuration of an OpenSearch node
22
3-
use serde_json::{Value, json};
3+
use serde_json::json;
44
use stackable_operator::{
55
builder::pod::container::FieldPathEnvVar, commons::networking::DomainName,
6+
k8s_openapi::DeepMerge,
67
};
7-
use tracing::warn;
88

99
use super::ValidatedCluster;
1010
use crate::{
@@ -15,6 +15,7 @@ use crate::{
1515
crd::v1alpha1,
1616
framework::{
1717
builder::pod::container::{EnvVarName, EnvVarSet},
18+
config_overrides::JsonConfigOverrides,
1819
product_logging::framework::STACKABLE_LOG_DIR,
1920
role_group_utils,
2021
types::{kubernetes::ServiceName, operator::RoleGroupName},
@@ -174,39 +175,30 @@ impl NodeConfig {
174175

175176
/// Creates the main OpenSearch configuration file in YAML format
176177
pub fn opensearch_config_file_content(&self) -> String {
177-
Self::to_yaml(self.opensearch_config())
178+
serde_yaml::to_string(&self.opensearch_config())
179+
.expect("serde_json::Value should always be serializable")
178180
}
179181

180-
pub fn opensearch_config(&self) -> serde_json::Map<String, Value> {
182+
pub fn opensearch_config(&self) -> serde_json::Value {
181183
let mut config = self.static_opensearch_config();
182184

183-
config.append(&mut self.tls_config());
185+
config.merge_from(self.tls_config());
184186

185-
for (setting, value) in &self
187+
let json_config_overrides: JsonConfigOverrides = self
186188
.role_group_config
187189
.config_overrides
188190
.opensearch_yml
189-
.overrides
190-
{
191-
let old_value = config.insert(setting.to_owned(), json!(value));
192-
if let Some(old_value) = old_value {
193-
warn!(
194-
"configOverrides: Configuration setting {setting:?} changed from {old_value} to {value:?}."
195-
);
196-
}
197-
}
198-
199-
// Ensure a deterministic result
200-
config.sort_keys();
191+
.clone()
192+
.into();
201193

202-
config
194+
json_config_overrides.apply(&config).into_owned()
203195
}
204196

205197
/// Creates the main OpenSearch configuration file as JSON map
206198
///
207199
/// The file should only contain cluster-wide configuration options. Node-specific options
208200
/// should be defined as environment variables.
209-
pub fn static_opensearch_config(&self) -> serde_json::Map<String, Value> {
201+
pub fn static_opensearch_config(&self) -> serde_json::Value {
210202
let mut config = serde_json::Map::new();
211203

212204
config.insert(
@@ -262,7 +254,7 @@ impl NodeConfig {
262254
}
263255
};
264256

265-
config
257+
json!(config)
266258
}
267259

268260
/// Distinguished name (DN) of the super admin certificate
@@ -271,7 +263,7 @@ impl NodeConfig {
271263
format!("CN=update-security-config.{}", self.cluster.uid)
272264
}
273265

274-
pub fn tls_config(&self) -> serde_json::Map<String, Value> {
266+
pub fn tls_config(&self) -> serde_json::Value {
275267
let mut config = serde_json::Map::new();
276268

277269
let opensearch_path_conf = self.opensearch_path_conf();
@@ -327,7 +319,7 @@ impl NodeConfig {
327319
);
328320
}
329321

330-
config
322+
json!(config)
331323
}
332324

333325
/// Creates environment variables for the OpenSearch configurations
@@ -396,13 +388,6 @@ impl NodeConfig {
396388
env_vars.merge(self.role_group_config.env_overrides.clone())
397389
}
398390

399-
fn to_yaml(kv: serde_json::Map<String, Value>) -> String {
400-
kv.iter()
401-
.map(|(key, value)| format!("{key}: {value}"))
402-
.collect::<Vec<_>>()
403-
.join("\n")
404-
}
405-
406391
/// Configuration for `discovery.type`
407392
///
408393
/// "zen" is the default if `discovery.type` is not set.
@@ -619,13 +604,15 @@ mod tests {
619604
termination_grace_period_seconds: 30,
620605
},
621606
config_overrides: v1alpha1::OpenSearchConfigOverrides {
622-
opensearch_yml: KeyValueConfigOverrides {
623-
overrides: test_config
624-
.config_settings
625-
.iter()
626-
.map(|(k, v)| (k.to_string(), Some(v.to_string())))
627-
.collect(),
628-
},
607+
opensearch_yml: v1alpha1::ConfigOverridesChoice::KeyValue(
608+
KeyValueConfigOverrides {
609+
overrides: test_config
610+
.config_settings
611+
.iter()
612+
.map(|(k, v)| (k.to_string(), Some(v.to_string())))
613+
.collect(),
614+
},
615+
),
629616
},
630617
env_overrides: EnvVarSet::new().with_values(
631618
test_config
@@ -712,22 +699,23 @@ mod tests {
712699

713700
assert_eq!(
714701
concat!(
715-
"cluster.name: \"my-opensearch-cluster\"\n",
716-
"discovery.type: \"zen\"\n",
717-
"network.host: \"0.0.0.0\"\n",
718-
"node.attr.role-group: \"data\"\n",
719-
"path.logs: \"/stackable/log/opensearch\"\n",
720-
"plugins.security.authcz.admin_dn: \"CN=update-security-config.0b1e30e6-326e-4c1a-868d-ad6598b49e8b\"\n",
721-
"plugins.security.nodes_dn: [\"CN=generated certificate for pod\"]\n",
702+
"cluster.name: my-opensearch-cluster\n",
703+
"discovery.type: zen\n",
704+
"network.host: 0.0.0.0\n",
705+
"node.attr.role-group: data\n",
706+
"path.logs: /stackable/log/opensearch\n",
707+
"plugins.security.authcz.admin_dn: CN=update-security-config.0b1e30e6-326e-4c1a-868d-ad6598b49e8b\n",
708+
"plugins.security.nodes_dn:\n",
709+
"- CN=generated certificate for pod\n",
722710
"plugins.security.ssl.http.enabled: true\n",
723-
"plugins.security.ssl.http.pemcert_filepath: \"/stackable/opensearch/config/tls/server/tls.crt\"\n",
724-
"plugins.security.ssl.http.pemkey_filepath: \"/stackable/opensearch/config/tls/server/tls.key\"\n",
725-
"plugins.security.ssl.http.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/server/ca.crt\"\n",
711+
"plugins.security.ssl.http.pemcert_filepath: /stackable/opensearch/config/tls/server/tls.crt\n",
712+
"plugins.security.ssl.http.pemkey_filepath: /stackable/opensearch/config/tls/server/tls.key\n",
713+
"plugins.security.ssl.http.pemtrustedcas_filepath: /stackable/opensearch/config/tls/server/ca.crt\n",
726714
"plugins.security.ssl.transport.enabled: true\n",
727-
"plugins.security.ssl.transport.pemcert_filepath: \"/stackable/opensearch/config/tls/internal/tls.crt\"\n",
728-
"plugins.security.ssl.transport.pemkey_filepath: \"/stackable/opensearch/config/tls/internal/tls.key\"\n",
729-
"plugins.security.ssl.transport.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/internal/ca.crt\"\n",
730-
"test: \"value\"",
715+
"plugins.security.ssl.transport.pemcert_filepath: /stackable/opensearch/config/tls/internal/tls.crt\n",
716+
"plugins.security.ssl.transport.pemkey_filepath: /stackable/opensearch/config/tls/internal/tls.key\n",
717+
"plugins.security.ssl.transport.pemtrustedcas_filepath: /stackable/opensearch/config/tls/internal/ca.crt\n",
718+
"test: value\n",
731719
)
732720
.to_owned(),
733721
node_config.opensearch_config_file_content()

rust/operator-binary/src/controller/build/role_builder.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,11 @@ impl<'a> RoleBuilder<'a> {
223223
v1alpha1::SecuritySettingsFileTypeContentValue { value },
224224
) = &file_type.content
225225
{
226-
data.insert(file_type.filename.to_owned(), value.to_string());
226+
data.insert(
227+
file_type.filename.to_owned(),
228+
serde_yaml::to_string(value)
229+
.expect("serde_json::Value should be serializable"),
230+
);
227231
}
228232
}
229233
}

0 commit comments

Comments
 (0)