Skip to content

Commit f60d49e

Browse files
committed
refactor: consolidate ValidatedRoleGroupConfig
1 parent 424a3a4 commit f60d49e

3 files changed

Lines changed: 63 additions & 22 deletions

File tree

rust/operator-binary/src/controller/validate.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
use std::collections::BTreeMap;
1+
use std::{collections::BTreeMap, str::FromStr};
22

33
use snafu::{ResultExt, Snafu};
44
use stackable_operator::{
55
commons::product_image_selection::{self},
66
config::merge::Merge,
77
role_utils::GenericRoleConfig,
88
utils::cluster_info::KubernetesClusterInfo,
9-
v2::controller_utils::{get_cluster_name, get_namespace, get_uid},
9+
v2::{
10+
builder::pod::container::{self, EnvVarName, EnvVarSet},
11+
controller_utils::{get_cluster_name, get_namespace, get_uid},
12+
},
1013
};
1114
use strum::IntoEnumIterator;
1215

@@ -48,6 +51,12 @@ pub enum Error {
4851

4952
#[snafu(display("failed to construct role-specific JVM arguments"))]
5053
ConstructJvmArgument { source: crate::config::jvm::Error },
54+
55+
#[snafu(display("the environment variable override name {name:?} is invalid"))]
56+
InvalidEnvVarName {
57+
source: container::Error,
58+
name: String,
59+
},
5160
}
5261

5362
pub fn validate_cluster(
@@ -102,12 +111,21 @@ pub fn validate_cluster(
102111
)
103112
.context(FailedToResolveConfigSnafu)?;
104113

114+
let rolegroup_ref =
115+
hbase.server_rolegroup_ref(hbase_role.to_string(), rolegroup_name.clone());
116+
105117
group_configs.insert(
106118
rolegroup_name.clone(),
107119
ValidatedRoleGroupConfig {
120+
replicas: hbase.replicas(&hbase_role, &rolegroup_ref),
108121
merged_config,
109122
config_overrides: merged_config_overrides(hbase, &hbase_role, &rolegroup_name),
110-
env_overrides: merged_env_overrides(hbase, &hbase_role, &rolegroup_name),
123+
env_overrides: env_var_set(merged_env_overrides(
124+
hbase,
125+
&hbase_role,
126+
&rolegroup_name,
127+
))?,
128+
pod_overrides: hbase.merged_pod_overrides(&hbase_role, &rolegroup_ref),
111129
non_heap_jvm_args: construct_role_specific_non_heap_jvm_args(
112130
hbase,
113131
&hbase_role,
@@ -285,6 +303,19 @@ fn merged_env_overrides(
285303
env_overrides
286304
}
287305

306+
/// Converts merged env override pairs into a type-safe [`EnvVarSet`], validating each name so that
307+
/// invalid environment variable names are rejected during validation instead of producing a broken
308+
/// Pod.
309+
fn env_var_set(env_overrides: BTreeMap<String, String>) -> Result<EnvVarSet, Error> {
310+
let mut set = EnvVarSet::new();
311+
for (name, value) in env_overrides {
312+
let env_var_name =
313+
EnvVarName::from_str(&name).context(InvalidEnvVarNameSnafu { name: name.clone() })?;
314+
set = set.with_value(&env_var_name, value);
315+
}
316+
Ok(set)
317+
}
318+
288319
#[cfg(test)]
289320
mod tests {
290321
use indoc::indoc;

rust/operator-binary/src/crd/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,12 @@ impl v1alpha1::HbaseCluster {
351351
})
352352
}
353353

354-
pub fn merge_pod_overrides(
354+
/// Returns the merged (role <- role group) pod template overrides for the given role group.
355+
pub fn merged_pod_overrides(
355356
&self,
356-
pod_template: &mut PodTemplateSpec,
357357
role: &HbaseRole,
358358
role_group_ref: &RoleGroupRef<Self>,
359-
) {
359+
) -> PodTemplateSpec {
360360
let (role_pod_overrides, role_group_pod_overrides) = match role {
361361
HbaseRole::Master => (
362362
self.spec
@@ -393,41 +393,40 @@ impl v1alpha1::HbaseCluster {
393393
),
394394
};
395395

396+
let mut merged = PodTemplateSpec::default();
396397
if let Some(rpo) = role_pod_overrides {
397-
pod_template.merge_from(rpo);
398+
merged.merge_from(rpo);
398399
}
399400
if let Some(rgpo) = role_group_pod_overrides {
400-
pod_template.merge_from(rgpo);
401+
merged.merge_from(rgpo);
401402
}
403+
merged
402404
}
403405

404406
pub fn replicas(
405407
&self,
406408
hbase_role: &HbaseRole,
407409
role_group_ref: &RoleGroupRef<Self>,
408-
) -> Option<i32> {
410+
) -> Option<u16> {
409411
match hbase_role {
410412
HbaseRole::Master => self
411413
.spec
412414
.masters
413415
.as_ref()
414416
.and_then(|r| r.role_groups.get(&role_group_ref.role_group))
415-
.and_then(|rg| rg.replicas)
416-
.map(i32::from),
417+
.and_then(|rg| rg.replicas),
417418
HbaseRole::RegionServer => self
418419
.spec
419420
.region_servers
420421
.as_ref()
421422
.and_then(|r| r.role_groups.get(&role_group_ref.role_group))
422-
.and_then(|rg| rg.replicas)
423-
.map(i32::from),
423+
.and_then(|rg| rg.replicas),
424424
HbaseRole::RestServer => self
425425
.spec
426426
.rest_servers
427427
.as_ref()
428428
.and_then(|r| r.role_groups.get(&role_group_ref.role_group))
429-
.and_then(|rg| rg.replicas)
430-
.map(i32::from),
429+
.and_then(|rg| rg.replicas),
431430
}
432431
}
433432

rust/operator-binary/src/hbase_controller.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ use stackable_operator::{
1919
commons::{product_image_selection::ResolvedProductImage, rbac::build_rbac_resources},
2020
constants::RESTART_CONTROLLER_ENABLED_LABEL,
2121
k8s_openapi::{
22+
DeepMerge,
2223
api::{
2324
apps::v1::{StatefulSet, StatefulSetSpec},
2425
core::v1::{
25-
ConfigMapVolumeSource, ContainerPort, EnvVar, Probe, Service, ServiceAccount,
26-
ServicePort, ServiceSpec, TCPSocketAction, Volume,
26+
ConfigMapVolumeSource, ContainerPort, EnvVar, PodTemplateSpec, Probe, Service,
27+
ServiceAccount, ServicePort, ServiceSpec, TCPSocketAction, Volume,
2728
},
2829
},
2930
apimachinery::pkg::{
@@ -54,6 +55,7 @@ use stackable_operator::{
5455
},
5556
v2::{
5657
HasName, HasUid, NameIsValidLabelValue,
58+
builder::pod::container::EnvVarSet,
5759
types::{
5860
kubernetes::{NamespaceName, Uid},
5961
operator::ClusterName,
@@ -218,12 +220,19 @@ pub struct ValidatedRoleConfig {
218220
}
219221

220222
/// Per-rolegroup configuration: the merged CRD config plus the merged
221-
/// (role <- role group) `configOverrides` and `envOverrides`.
223+
/// (role <- role group) `configOverrides`, `envOverrides` and `podOverrides`.
224+
///
225+
/// This carries every override channel so that the build step is a pure function of
226+
/// [`ValidatedCluster`] and never has to reach back into the raw `HbaseCluster`.
222227
#[derive(Clone, Debug)]
223228
pub struct ValidatedRoleGroupConfig {
229+
/// The desired number of replicas (`None` lets Kubernetes default to 1).
230+
pub replicas: Option<u16>,
224231
pub merged_config: AnyServiceConfig,
225232
pub config_overrides: v1alpha1::HbaseConfigOverrides,
226-
pub env_overrides: BTreeMap<String, String>,
233+
pub env_overrides: EnvVarSet,
234+
/// Merged (role <- role group) pod template overrides.
235+
pub pod_overrides: PodTemplateSpec,
227236
/// Pre-resolved role-specific non-heap JVM args (operator-generated + role/role-group overrides).
228237
pub non_heap_jvm_args: String,
229238
}
@@ -713,7 +722,9 @@ fn build_rolegroup_statefulset(
713722
("HBASE_CONF_DIR".to_string(), CONFIG_DIR_NAME.to_string()),
714723
("HADOOP_CONF_DIR".to_string(), CONFIG_DIR_NAME.to_string()),
715724
]);
716-
env_map.extend(validated_rg_config.env_overrides.clone());
725+
for env_var in validated_rg_config.env_overrides.clone() {
726+
env_map.insert(env_var.name, env_var.value.unwrap_or_default());
727+
}
717728
let mut merged_env = merged_env(&env_map);
718729
// This env var is set for all roles to avoid bash's "unbound variable" errors
719730
merged_env.extend([
@@ -904,7 +915,7 @@ fn build_rolegroup_statefulset(
904915

905916
let mut pod_template = pod_builder.build_template();
906917

907-
hbase.merge_pod_overrides(&mut pod_template, hbase_role, rolegroup_ref);
918+
pod_template.merge_from(validated_rg_config.pod_overrides.clone());
908919

909920
let metadata = ObjectMetaBuilder::new()
910921
.name_and_namespace(hbase)
@@ -931,7 +942,7 @@ fn build_rolegroup_statefulset(
931942

932943
let statefulset_spec = StatefulSetSpec {
933944
pod_management_policy: Some("Parallel".to_string()),
934-
replicas: hbase.replicas(hbase_role, rolegroup_ref),
945+
replicas: validated_rg_config.replicas.map(i32::from),
935946
selector: LabelSelector {
936947
match_labels: Some(statefulset_match_labels.into()),
937948
..LabelSelector::default()

0 commit comments

Comments
 (0)