Skip to content

Commit 12bab82

Browse files
committed
refactor: introduce proper framework module split
1 parent 797da4a commit 12bab82

9 files changed

Lines changed: 296 additions & 166 deletions

File tree

rust/operator-binary/src/container.rs

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use stackable_operator::{
5353
},
5454
role_utils::RoleGroupRef,
5555
utils::{COMMON_BASH_TRAP_FUNCTIONS, cluster_info::KubernetesClusterInfo},
56+
v2::builder::pod::container::EnvVarSet,
5657
};
5758
use strum::{Display, EnumDiscriminants, IntoStaticStr};
5859

@@ -218,7 +219,7 @@ impl ContainerConfig {
218219
rolegroup_ref: &RoleGroupRef<v1alpha1::HdfsCluster>,
219220
resolved_product_image: &ResolvedProductImage,
220221
merged_config: &AnyNodeConfig,
221-
env_overrides: Option<&BTreeMap<String, String>>,
222+
env_overrides: Option<&EnvVarSet>,
222223
zk_config_map_name: &str,
223224
namenode_podrefs: &[HdfsPodRef],
224225
labels: &Labels,
@@ -472,7 +473,7 @@ impl ContainerConfig {
472473
rolegroup_ref: &RoleGroupRef<v1alpha1::HdfsCluster>,
473474
resolved_product_image: &ResolvedProductImage,
474475
zookeeper_config_map_name: &str,
475-
env_overrides: Option<&BTreeMap<String, String>>,
476+
env_overrides: Option<&EnvVarSet>,
476477
merged_config: &AnyNodeConfig,
477478
labels: &Labels,
478479
) -> Result<Container, Error> {
@@ -533,7 +534,7 @@ impl ContainerConfig {
533534
role_group: &str,
534535
resolved_product_image: &ResolvedProductImage,
535536
zookeeper_config_map_name: &str,
536-
env_overrides: Option<&BTreeMap<String, String>>,
537+
env_overrides: Option<&EnvVarSet>,
537538
namenode_podrefs: &[HdfsPodRef],
538539
merged_config: &AnyNodeConfig,
539540
labels: &Labels,
@@ -871,7 +872,7 @@ impl ContainerConfig {
871872
hdfs: &v1alpha1::HdfsCluster,
872873
role_group: &str,
873874
zookeeper_config_map_name: &str,
874-
env_overrides: Option<&BTreeMap<String, String>>,
875+
env_overrides: Option<&EnvVarSet>,
875876
resources: Option<&ResourceRequirements>,
876877
) -> Result<Vec<EnvVar>, Error> {
877878
// Maps env var name to env var object. This allows env_overrides to work
@@ -963,11 +964,12 @@ impl ContainerConfig {
963964
);
964965

965966
// Overrides need to come last
966-
let mut env_override_vars: BTreeMap<String, EnvVar> =
967-
Self::transform_env_overrides_to_env_vars(env_overrides)
968-
.into_iter()
969-
.map(|env_var| (env_var.name.clone(), env_var))
970-
.collect();
967+
let mut env_override_vars: BTreeMap<String, EnvVar> = env_overrides
968+
.cloned()
969+
.unwrap_or_default()
970+
.into_iter()
971+
.map(|env_var| (env_var.name.clone(), env_var))
972+
.collect();
971973

972974
env.append(&mut env_override_vars);
973975

@@ -1303,22 +1305,6 @@ impl ContainerConfig {
13031305
}
13041306
}
13051307

1306-
/// Transform the ProductConfig map structure to a Vector of env vars.
1307-
fn transform_env_overrides_to_env_vars(
1308-
env_overrides: Option<&BTreeMap<String, String>>,
1309-
) -> Vec<EnvVar> {
1310-
env_overrides
1311-
.cloned()
1312-
.unwrap_or_default()
1313-
.into_iter()
1314-
.map(|(k, v)| EnvVar {
1315-
name: k,
1316-
value: Some(v),
1317-
..EnvVar::default()
1318-
})
1319-
.collect()
1320-
}
1321-
13221308
/// Common shared or required container env variables.
13231309
fn shared_env_vars(hadoop_conf_dir: &str, zk_config_map_name: &str) -> Vec<EnvVar> {
13241310
vec![

rust/operator-binary/src/controller/build/properties/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,13 @@ spec:
121121
}
122122

123123
pub fn validated_cluster() -> ValidatedCluster {
124-
validate_cluster(&minimal_hdfs(), "oci.example.org", None)
125-
.expect("validate should succeed for the minimal fixture")
124+
validate_cluster(
125+
&minimal_hdfs(),
126+
"oci.example.org",
127+
crate::controller::dereference::DereferencedObjects {
128+
hdfs_opa_config: None,
129+
},
130+
)
131+
.expect("validate should succeed for the minimal fixture")
126132
}
127133
}

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,18 @@ pub struct ValidatedRoleConfig {
205205
}
206206

207207
/// Per-rolegroup configuration: the merged CRD config plus the merged
208-
/// (role <- role group) `configOverrides` and `envOverrides`.
209-
#[derive(Clone, Debug)]
210-
pub struct ValidatedRoleGroupConfig {
211-
/// The number of replicas (pods) for this role group, used to derive the
212-
/// per-pod [`HdfsPodRef`]s via [`ValidatedCluster::pod_refs`].
213-
pub replicas: u16,
214-
pub config: AnyNodeConfig,
215-
pub config_overrides: v1alpha1::HdfsConfigOverrides,
216-
pub env_overrides: BTreeMap<String, String>,
217-
}
208+
/// (role <- role group) `configOverrides`, `envOverrides`, `cliOverrides` and
209+
/// `podOverrides`.
210+
///
211+
/// This is the local-`framework` [`RoleGroupConfig`](crate::framework::role_utils::RoleGroupConfig)
212+
/// specialised for HDFS: the validated config is the per-role [`AnyNodeConfig`],
213+
/// the product-specific common config is [`JavaCommonConfig`] (whose JVM-argument
214+
/// merge is fallible, hence the vendored framework variant), and the config
215+
/// overrides are [`v1alpha1::HdfsConfigOverrides`]. The `replicas` field is used
216+
/// to derive the per-pod [`HdfsPodRef`]s via [`ValidatedCluster::pod_refs`] and
217+
/// `env_overrides` is the typed [`EnvVarSet`](stackable_operator::v2::builder::pod::container::EnvVarSet).
218+
pub type ValidatedRoleGroupConfig = crate::framework::role_utils::RoleGroupConfig<
219+
AnyNodeConfig,
220+
stackable_operator::role_utils::JavaCommonConfig,
221+
v1alpha1::HdfsConfigOverrides,
222+
>;

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

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
//! The validate step in the HdfsCluster controller.
22
//!
33
//! Synchronously merges and validates the cluster spec into the typed [`ValidatedCluster`]
4-
//! consumed by `controller::build::*`. Config fragments are merged and validated via
5-
//! [`HdfsNodeRole::merged_config`], and the per-file `configOverrides` / `envOverrides`
6-
//! are merged here (role group wins).
4+
//! consumed by `controller::build::*`. Each role group is merged and validated via the
5+
//! local-`framework` [`with_validated_config`], which folds the config fragment
6+
//! (default <- role <- role group) together with the `configOverrides`, `envOverrides`,
7+
//! `cliOverrides` and `podOverrides` (role group wins) into a single
8+
//! [`RoleGroupConfig`](crate::framework::role_utils::RoleGroupConfig).
79
810
use std::collections::BTreeMap;
911

1012
use snafu::{ResultExt, Snafu};
1113
use stackable_operator::{
1214
commons::product_image_selection,
13-
config::merge::Merge,
14-
role_utils::{GenericRoleConfig, JavaCommonConfig, Role, RoleGroup},
15+
config::{fragment::FromFragment, merge::Merge},
16+
role_utils::{GenericRoleConfig, JavaCommonConfig, Role},
1517
v2::controller_utils::{get_cluster_name, get_namespace, get_uid},
1618
};
1719
use strum::IntoEnumIterator;
1820

1921
use crate::{
2022
controller::{
2123
ValidatedCluster, ValidatedClusterConfig, ValidatedRoleConfig, ValidatedRoleGroupConfig,
24+
dereference::DereferencedObjects,
2225
},
23-
crd::{HdfsNodeRole, v1alpha1},
24-
security::opa::HdfsOpaConfig,
26+
crd::{
27+
AnyNodeConfig, DataNodeConfigFragment, HdfsNodeRole, JournalNodeConfigFragment,
28+
NameNodeConfigFragment, v1alpha1,
29+
},
30+
framework::role_utils::with_validated_config,
2531
};
2632

2733
const CONTAINER_IMAGE_BASE_NAME: &str = "hadoop";
@@ -48,16 +54,18 @@ pub enum Error {
4854
source: stackable_operator::v2::controller_utils::Error,
4955
},
5056

51-
#[snafu(display("failed to resolve and merge config for role and role group"))]
52-
FailedToResolveConfig { source: crate::crd::Error },
57+
#[snafu(display("failed to merge and validate the role group config"))]
58+
ValidateRoleGroupConfig {
59+
source: crate::framework::role_utils::Error,
60+
},
5361
}
5462

5563
pub fn validate_cluster(
5664
hdfs: &v1alpha1::HdfsCluster,
5765
image_repository: &str,
58-
hdfs_opa_config: Option<HdfsOpaConfig>,
66+
dereferenced_objects: DereferencedObjects,
5967
) -> Result<ValidatedCluster, Error> {
60-
let resolved_product_image = hdfs
68+
let image: product_image_selection::ResolvedProductImage = hdfs
6169
.spec
6270
.image
6371
.resolve(
@@ -69,6 +77,7 @@ pub fn validate_cluster(
6977

7078
let mut role_groups = BTreeMap::new();
7179
let mut role_configs = BTreeMap::new();
80+
let cluster_name = get_cluster_name(hdfs).context(GetClusterNameSnafu)?;
7281

7382
for hdfs_role in HdfsNodeRole::iter() {
7483
if let Some(GenericRoleConfig {
@@ -79,83 +88,87 @@ pub fn validate_cluster(
7988
}
8089

8190
let group_configs = match hdfs_role {
82-
HdfsNodeRole::Name => {
83-
validate_role_group_configs(hdfs, hdfs_role, hdfs.spec.name_nodes.as_ref())?
84-
}
85-
HdfsNodeRole::Data => {
86-
validate_role_group_configs(hdfs, hdfs_role, hdfs.spec.data_nodes.as_ref())?
87-
}
88-
HdfsNodeRole::Journal => {
89-
validate_role_group_configs(hdfs, hdfs_role, hdfs.spec.journal_nodes.as_ref())?
90-
}
91+
HdfsNodeRole::Name => validate_role_group_configs(
92+
hdfs.spec.name_nodes.as_ref(),
93+
NameNodeConfigFragment::default_config(cluster_name.as_ref(), &hdfs_role),
94+
AnyNodeConfig::Name,
95+
)?,
96+
HdfsNodeRole::Data => validate_role_group_configs(
97+
hdfs.spec.data_nodes.as_ref(),
98+
DataNodeConfigFragment::default_config(cluster_name.as_ref(), &hdfs_role),
99+
AnyNodeConfig::Data,
100+
)?,
101+
HdfsNodeRole::Journal => validate_role_group_configs(
102+
hdfs.spec.journal_nodes.as_ref(),
103+
JournalNodeConfigFragment::default_config(cluster_name.as_ref(), &hdfs_role),
104+
AnyNodeConfig::Journal,
105+
)?,
91106
};
92107

93108
role_groups.insert(hdfs_role, group_configs);
94109
}
95110

96-
let cluster_name = get_cluster_name(hdfs).context(GetClusterNameSnafu)?;
97111
let namespace = get_namespace(hdfs).context(GetClusterNamespaceSnafu)?;
98112
let uid = get_uid(hdfs).context(GetClusterUidSnafu)?;
99113

100114
Ok(ValidatedCluster::new(
101115
cluster_name,
102116
namespace,
103117
uid,
104-
resolved_product_image,
105-
ValidatedClusterConfig::resolve(hdfs, hdfs_opa_config),
118+
image,
119+
ValidatedClusterConfig::resolve(hdfs, dereferenced_objects.hdfs_opa_config),
106120
role_groups,
107121
role_configs,
108122
))
109123
}
110124

111125
/// Validates every role group of a role into a map keyed by role group name.
112126
///
127+
/// Each role group is merged and validated via the local-`framework`
128+
/// [`with_validated_config`], which folds the CRD config fragment (default <-
129+
/// role <- role group) plus the `configOverrides`, `envOverrides`, `cliOverrides`
130+
/// and `podOverrides` (role group wins) into a single
131+
/// [`RoleGroupConfig`](crate::framework::role_utils::RoleGroupConfig). The
132+
/// concrete per-role validated config is wrapped into [`AnyNodeConfig`] via `wrap`.
133+
///
113134
/// Returns an empty map if the role is not configured.
114-
fn validate_role_group_configs<C>(
115-
hdfs: &v1alpha1::HdfsCluster,
116-
hdfs_role: HdfsNodeRole,
117-
role: Option<&Role<C, v1alpha1::HdfsConfigOverrides, GenericRoleConfig, JavaCommonConfig>>,
118-
) -> Result<BTreeMap<String, ValidatedRoleGroupConfig>, Error> {
135+
fn validate_role_group_configs<Config, ValidatedConfig>(
136+
role: Option<&Role<Config, v1alpha1::HdfsConfigOverrides, GenericRoleConfig, JavaCommonConfig>>,
137+
default_config: Config,
138+
wrap: fn(ValidatedConfig) -> AnyNodeConfig,
139+
) -> Result<BTreeMap<String, ValidatedRoleGroupConfig>, Error>
140+
where
141+
Config: Clone + Merge,
142+
ValidatedConfig: FromFragment<Fragment = Config>,
143+
{
119144
let Some(role) = role else {
120145
return Ok(BTreeMap::new());
121146
};
122147

123148
role.role_groups
124149
.iter()
125150
.map(|(role_group_name, role_group)| {
126-
let validated =
127-
validate_role_group_config(hdfs, hdfs_role, role, role_group_name, role_group)?;
151+
let validated = with_validated_config::<
152+
ValidatedConfig,
153+
JavaCommonConfig,
154+
Config,
155+
GenericRoleConfig,
156+
v1alpha1::HdfsConfigOverrides,
157+
>(role_group, role, &default_config)
158+
.context(ValidateRoleGroupConfigSnafu)?;
159+
160+
// Re-wrap the per-role validated config into the role-agnostic
161+
// `AnyNodeConfig`; the merged overrides carry over unchanged.
162+
let validated = ValidatedRoleGroupConfig {
163+
replicas: validated.replicas,
164+
config: wrap(validated.config),
165+
config_overrides: validated.config_overrides,
166+
env_overrides: validated.env_overrides,
167+
cli_overrides: validated.cli_overrides,
168+
pod_overrides: validated.pod_overrides,
169+
product_specific_common_config: validated.product_specific_common_config,
170+
};
128171
Ok((role_group_name.clone(), validated))
129172
})
130173
.collect()
131174
}
132-
133-
/// Validates a single role group into a [`ValidatedRoleGroupConfig`]: merges and
134-
/// validates the CRD config via [`HdfsNodeRole::merged_config`] and merges the
135-
/// role-level and role-group-level `configOverrides` and `envOverrides` (the role
136-
/// group wins).
137-
fn validate_role_group_config<C>(
138-
hdfs: &v1alpha1::HdfsCluster,
139-
hdfs_role: HdfsNodeRole,
140-
role: &Role<C, v1alpha1::HdfsConfigOverrides, GenericRoleConfig, JavaCommonConfig>,
141-
role_group_name: &str,
142-
role_group: &RoleGroup<C, JavaCommonConfig, v1alpha1::HdfsConfigOverrides>,
143-
) -> Result<ValidatedRoleGroupConfig, Error> {
144-
let config = hdfs_role
145-
.merged_config(hdfs, role_group_name)
146-
.context(FailedToResolveConfigSnafu)?;
147-
148-
let mut config_overrides = role_group.config.config_overrides.clone();
149-
config_overrides.merge(&role.config.config_overrides);
150-
151-
let mut env_overrides = BTreeMap::new();
152-
env_overrides.extend(role.config.env_overrides.clone());
153-
env_overrides.extend(role_group.config.env_overrides.clone());
154-
155-
Ok(ValidatedRoleGroupConfig {
156-
replicas: role_group.replicas.unwrap_or_default(),
157-
config,
158-
config_overrides,
159-
env_overrides,
160-
})
161-
}

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

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ use stackable_operator::{
2525
},
2626
crd::listener,
2727
deep_merger::ObjectOverrides,
28-
k8s_openapi::{
29-
api::core::v1::{Pod, PodTemplateSpec},
30-
apimachinery::pkg::api::resource::Quantity,
31-
},
28+
k8s_openapi::{api::core::v1::Pod, apimachinery::pkg::api::resource::Quantity},
3229
kube::{CustomResource, ResourceExt, runtime::reflector::ObjectRef},
3330
kvp::{LabelError, Labels},
3431
product_logging::{
@@ -372,44 +369,6 @@ impl v1alpha1::HdfsCluster {
372369
.context(MergeJvmArgumentOverridesSnafu)
373370
}
374371

375-
pub fn pod_overrides_for_role(&self, role: &HdfsNodeRole) -> Option<&PodTemplateSpec> {
376-
match role {
377-
HdfsNodeRole::Name => self
378-
.spec
379-
.name_nodes
380-
.as_ref()
381-
.map(|n| &n.config.pod_overrides),
382-
HdfsNodeRole::Data => self
383-
.spec
384-
.data_nodes
385-
.as_ref()
386-
.map(|n| &n.config.pod_overrides),
387-
HdfsNodeRole::Journal => self
388-
.spec
389-
.journal_nodes
390-
.as_ref()
391-
.map(|n| &n.config.pod_overrides),
392-
}
393-
}
394-
395-
pub fn pod_overrides_for_role_group(
396-
&self,
397-
role: &HdfsNodeRole,
398-
role_group: &str,
399-
) -> Option<&PodTemplateSpec> {
400-
match role {
401-
HdfsNodeRole::Name => self
402-
.namenode_rolegroup(role_group)
403-
.map(|r| &r.config.pod_overrides),
404-
HdfsNodeRole::Data => self
405-
.datanode_rolegroup(role_group)
406-
.map(|r| &r.config.pod_overrides),
407-
HdfsNodeRole::Journal => self
408-
.journalnode_rolegroup(role_group)
409-
.map(|r| &r.config.pod_overrides),
410-
}
411-
}
412-
413372
pub fn rolegroup_ref(
414373
&self,
415374
role_name: impl Into<String>,

0 commit comments

Comments
 (0)