Skip to content

Commit ceac76c

Browse files
maltesanderclaude
andcommitted
refactor: Vendor role_utils framework module
Add framework/role_utils.rs mirroring stackable_operator::v2::role_utils from the smooth-operator branch (as trino-operator does): RoleGroupConfig plus with_validated_config, which merges default <- role <- rolegroup config fragments and validates them via FromFragment. Gated with allow(dead_code) until the reconciler is switched to produce a ValidatedCluster in the following commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ccc3873 commit ceac76c

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

rust/operator-binary/src/framework.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@
77
//! Once the upstream `v2` API has stabilized, these modules should be replaced
88
//! by direct usage of `stackable_operator::v2`.
99
10+
// Wired into the reconciler in a follow-up commit (validate -> ValidatedCluster).
11+
#[allow(dead_code)]
12+
pub mod role_utils;
1013
pub mod writer;
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//! Vendored variant of `stackable_operator::v2::role_utils` from the
2+
//! `smooth-operator` branch, with simplifications appropriate for
3+
//! zookeeper-operator.
4+
//!
5+
//! Differences from upstream:
6+
//! - `env_overrides` is `BTreeMap<String, String>` instead of `EnvVarSet`.
7+
//! - No `cli_overrides_to_vec` helper, `ResourceNames`, or service-account helpers.
8+
//! - The `CommonConfig` (a.k.a. `product_specific_common_config`) does NOT need to
9+
//! implement `Merge`. ZooKeeper uses `JavaCommonConfig`, which intentionally does
10+
//! not implement `Merge` because its inner `JvmArgumentOverrides::try_merge` is
11+
//! fallible (regex validation). The `RoleGroupConfig::product_specific_common_config`
12+
//! field here simply carries the role-group level value through.
13+
//!
14+
//! Replace with `stackable_operator::v2::role_utils::*` once upstream publishes
15+
//! the module.
16+
17+
use std::collections::BTreeMap;
18+
19+
use serde::Serialize;
20+
use stackable_operator::{
21+
config::{
22+
fragment::{self, FromFragment},
23+
merge::{Merge, merge},
24+
},
25+
k8s_openapi::{DeepMerge, api::core::v1::PodTemplateSpec},
26+
role_utils::{Role, RoleGroup},
27+
schemars::JsonSchema,
28+
};
29+
30+
/// A validated, merged view of a `RoleGroup`.
31+
///
32+
/// Mirrors `stackable_operator::v2::role_utils::RoleGroupConfig` on the
33+
/// `smooth-operator` branch, with `env_overrides: BTreeMap<String, String>`
34+
/// instead of the upstream `EnvVarSet`.
35+
#[derive(Clone, Debug, PartialEq)]
36+
pub struct RoleGroupConfig<Config, CommonConfig, ConfigOverrides> {
37+
pub replicas: u16,
38+
pub config: Config,
39+
pub config_overrides: ConfigOverrides,
40+
pub env_overrides: BTreeMap<String, String>,
41+
pub cli_overrides: BTreeMap<String, String>,
42+
pub pod_overrides: PodTemplateSpec,
43+
pub product_specific_common_config: CommonConfig,
44+
}
45+
46+
/// Merges and validates the `RoleGroup` with the given `role` and `default_config`,
47+
/// returning a `RoleGroupConfig`.
48+
///
49+
/// Merge order matches `with_validated_config` on `smooth-operator`:
50+
/// - `Config` (Fragment): `default_config <- role.config <- rg.config` via `Merge::merge`,
51+
/// then validated to `ValidatedConfig` via `FromFragment`.
52+
/// - `ConfigOverrides`: `role.config_overrides <- rg.config_overrides` via `Merge::merge`.
53+
/// - `env_overrides` / `cli_overrides`: `extend` (rg keys overwrite role keys).
54+
/// - `pod_overrides`: `DeepMerge::merge_from` (rg overrides role).
55+
/// - `product_specific_common_config`: passes through the role-group level value
56+
/// (see module docs for rationale).
57+
pub fn with_validated_config<ValidatedConfig, CommonConfig, Config, RoleConfig, ConfigOverrides>(
58+
role_group: &RoleGroup<Config, CommonConfig, ConfigOverrides>,
59+
role: &Role<Config, ConfigOverrides, RoleConfig, CommonConfig>,
60+
default_config: &Config,
61+
) -> Result<
62+
RoleGroupConfig<ValidatedConfig, CommonConfig, ConfigOverrides>,
63+
fragment::ValidationError,
64+
>
65+
where
66+
ValidatedConfig: FromFragment<Fragment = Config>,
67+
CommonConfig: Clone + Default + JsonSchema + Serialize,
68+
Config: Clone + Merge,
69+
RoleConfig: Default + JsonSchema + Serialize,
70+
ConfigOverrides: Clone + Default + JsonSchema + Merge + Serialize,
71+
{
72+
let validated_config = validate_config(role_group, role, default_config)?;
73+
Ok(RoleGroupConfig {
74+
replicas: role_group.replicas.unwrap_or(1),
75+
config: validated_config,
76+
config_overrides: merged_config_overrides(
77+
&role.config.config_overrides,
78+
role_group.config.config_overrides.clone(),
79+
),
80+
env_overrides: merged_env_overrides(
81+
role.config
82+
.env_overrides
83+
.iter()
84+
.map(|(k, v)| (k.clone(), v.clone()))
85+
.collect(),
86+
role_group
87+
.config
88+
.env_overrides
89+
.iter()
90+
.map(|(k, v)| (k.clone(), v.clone()))
91+
.collect(),
92+
),
93+
cli_overrides: merged_cli_overrides(
94+
role.config.cli_overrides.clone(),
95+
role_group.config.cli_overrides.clone(),
96+
),
97+
pod_overrides: merged_pod_overrides(
98+
role.config.pod_overrides.clone(),
99+
role_group.config.pod_overrides.clone(),
100+
),
101+
product_specific_common_config: role_group.config.product_specific_common_config.clone(),
102+
})
103+
}
104+
105+
fn validate_config<ValidatedConfig, CommonConfig, Config, RoleConfig, ConfigOverrides>(
106+
role_group: &RoleGroup<Config, CommonConfig, ConfigOverrides>,
107+
role: &Role<Config, ConfigOverrides, RoleConfig, CommonConfig>,
108+
default_config: &Config,
109+
) -> Result<ValidatedConfig, fragment::ValidationError>
110+
where
111+
ValidatedConfig: FromFragment<Fragment = Config>,
112+
CommonConfig: Default + JsonSchema + Serialize,
113+
Config: Clone + Merge,
114+
RoleConfig: Default + JsonSchema + Serialize,
115+
ConfigOverrides: Default + JsonSchema + Serialize,
116+
{
117+
role_group.validate_config(role, default_config)
118+
}
119+
120+
fn merged_config_overrides<ConfigOverrides>(
121+
role_config_overrides: &ConfigOverrides,
122+
role_group_config_overrides: ConfigOverrides,
123+
) -> ConfigOverrides
124+
where
125+
ConfigOverrides: Merge,
126+
{
127+
merge(role_group_config_overrides, role_config_overrides)
128+
}
129+
130+
fn merged_env_overrides(
131+
role_env_overrides: BTreeMap<String, String>,
132+
role_group_env_overrides: BTreeMap<String, String>,
133+
) -> BTreeMap<String, String> {
134+
let mut merged = role_env_overrides;
135+
merged.extend(role_group_env_overrides);
136+
merged
137+
}
138+
139+
fn merged_cli_overrides(
140+
role_cli_overrides: BTreeMap<String, String>,
141+
role_group_cli_overrides: BTreeMap<String, String>,
142+
) -> BTreeMap<String, String> {
143+
let mut merged = role_cli_overrides;
144+
merged.extend(role_group_cli_overrides);
145+
merged
146+
}
147+
148+
fn merged_pod_overrides(
149+
role_pod_overrides: PodTemplateSpec,
150+
role_group_pod_overrides: PodTemplateSpec,
151+
) -> PodTemplateSpec {
152+
let mut merged = role_pod_overrides;
153+
merged.merge_from(role_group_pod_overrides);
154+
merged
155+
}

0 commit comments

Comments
 (0)