Skip to content

Commit 6da580c

Browse files
adwk67claude
andcommitted
refactor: extract validate_cluster into controller::validate module
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 30acbeb commit 6da580c

2 files changed

Lines changed: 152 additions & 130 deletions

File tree

rust/operator-binary/src/controller.rs

Lines changed: 8 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Ensures that `Pod`s are configured and running for each [`v1alpha1::HiveCluster`]
22
33
pub mod dereference;
4+
pub mod validate;
45

56
use std::{
6-
borrow::Cow,
77
collections::{BTreeMap, HashMap},
88
hash::Hasher,
99
sync::Arc,
@@ -64,7 +64,6 @@ use stackable_operator::{
6464
kvp::{Labels, ObjectLabels},
6565
logging::controller::ReconcilerError,
6666
memory::{BinaryMultiple, MemoryQuantity},
67-
product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config},
6867
product_logging::{
6968
self,
7069
framework::{
@@ -75,7 +74,7 @@ use stackable_operator::{
7574
CustomContainerLogConfig,
7675
},
7776
},
78-
role_utils::{GenericRoleConfig, RoleGroupRef},
77+
role_utils::RoleGroupRef,
7978
shared::time::Duration,
8079
status::condition::{
8180
compute_conditions, operations::ClusterOperationsConditionBuilder,
@@ -101,7 +100,7 @@ use crate::{
101100
STACKABLE_LOG_CONFIG_MOUNT_DIR, STACKABLE_LOG_CONFIG_MOUNT_DIR_NAME, STACKABLE_LOG_DIR,
102101
STACKABLE_LOG_DIR_NAME,
103102
databases::{MetadataDatabaseConnection, derby_driver_class},
104-
v1alpha1::{self, HiveMetastoreRoleConfig},
103+
v1alpha1,
105104
},
106105
discovery::{self},
107106
kerberos::{
@@ -130,41 +129,13 @@ pub struct Ctx {
130129
pub operator_environment: OperatorEnvironmentOptions,
131130
}
132131

133-
/// Per-role configuration extracted during validation.
134-
#[derive(Clone, Debug)]
135-
pub struct ValidatedRoleConfig {
136-
pub pdb: stackable_operator::commons::pdb::PdbConfig,
137-
pub listener_class: String,
138-
}
139-
140-
/// Per-rolegroup configuration: the merged CRD config plus the product-config properties.
141-
#[derive(Clone, Debug)]
142-
pub struct ValidatedRoleGroupConfig {
143-
pub merged_config: MetaStoreConfig,
144-
pub product_config_properties: HashMap<PropertyNameKind, BTreeMap<String, String>>,
145-
}
146-
147-
pub use crate::controller::dereference::DereferencedObjects;
148-
149-
/// The validated cluster: proves that product-config validation and config merging
150-
/// succeeded for every role and role group before any resources are created.
151-
#[derive(Clone, Debug)]
152-
pub struct ValidatedHiveCluster {
153-
pub image: ResolvedProductImage,
154-
pub role_groups: BTreeMap<HiveRole, BTreeMap<String, ValidatedRoleGroupConfig>>,
155-
pub role_configs: BTreeMap<HiveRole, ValidatedRoleConfig>,
156-
}
157-
158132
#[derive(Snafu, Debug, EnumDiscriminants)]
159133
#[strum_discriminants(derive(strum::IntoStaticStr))]
160134
#[allow(clippy::enum_variant_names)]
161135
pub enum Error {
162136
#[snafu(display("object defines no namespace"))]
163137
ObjectHasNoNamespace,
164138

165-
#[snafu(display("object defines no metastore role"))]
166-
NoMetaStoreRole,
167-
168139
#[snafu(display("failed to apply Service for {rolegroup}"))]
169140
ApplyRoleGroupService {
170141
source: stackable_operator::cluster_resources::Error,
@@ -189,16 +160,6 @@ pub enum Error {
189160
rolegroup: RoleGroupRef<v1alpha1::HiveCluster>,
190161
},
191162

192-
#[snafu(display("failed to generate product config"))]
193-
GenerateProductConfig {
194-
source: stackable_operator::product_config_utils::Error,
195-
},
196-
197-
#[snafu(display("invalid product config"))]
198-
InvalidProductConfig {
199-
source: stackable_operator::product_config_utils::Error,
200-
},
201-
202163
#[snafu(display("object is missing metadata to build owner reference"))]
203164
ObjectMissingMetadataForOwnerRef {
204165
source: stackable_operator::builder::meta::Error,
@@ -227,9 +188,6 @@ pub enum Error {
227188
))]
228189
S3TlsNoVerificationNotSupported,
229190

230-
#[snafu(display("failed to resolve and merge resource config for role and role group"))]
231-
FailedToResolveResourceConfig { source: crate::crd::Error },
232-
233191
#[snafu(display("failed to create hive container [{name}]"))]
234192
FailedToCreateHiveContainer {
235193
source: stackable_operator::builder::pod::container::Error,
@@ -351,6 +309,9 @@ pub enum Error {
351309
source: crate::controller::dereference::Error,
352310
},
353311

312+
#[snafu(display("failed to validate cluster configuration"))]
313+
Validate { source: validate::Error },
314+
354315
#[snafu(display("failed to build TLS certificate SecretClass Volume"))]
355316
TlsCertSecretClassVolumeBuild {
356317
source: stackable_operator::builder::pod::volume::SecretOperatorVolumeSourceBuilderError,
@@ -364,90 +325,6 @@ impl ReconcilerError for Error {
364325
}
365326
}
366327

367-
fn validate_cluster(
368-
hive: &v1alpha1::HiveCluster,
369-
dereferenced: &DereferencedObjects,
370-
product_config_manager: &ProductConfigManager,
371-
) -> Result<ValidatedHiveCluster> {
372-
let role = hive.spec.metastore.as_ref().context(NoMetaStoreRoleSnafu)?;
373-
374-
let validated_config = validate_all_roles_and_groups_config(
375-
&dereferenced.resolved_product_image.product_version,
376-
&transform_all_roles_to_config(
377-
hive,
378-
&[(
379-
HiveRole::MetaStore.to_string(),
380-
(
381-
vec![
382-
PropertyNameKind::Env,
383-
PropertyNameKind::Cli,
384-
PropertyNameKind::File(HIVE_SITE_XML.to_string()),
385-
PropertyNameKind::File(JVM_SECURITY_PROPERTIES_FILE.to_string()),
386-
],
387-
role.clone(),
388-
),
389-
)]
390-
.into(),
391-
)
392-
.context(GenerateProductConfigSnafu)?,
393-
product_config_manager,
394-
false,
395-
false,
396-
)
397-
.context(InvalidProductConfigSnafu)?;
398-
399-
let mut role_groups = BTreeMap::new();
400-
let mut role_configs = BTreeMap::new();
401-
402-
let metastore_config = validated_config
403-
.get(&HiveRole::MetaStore.to_string())
404-
.map(Cow::Borrowed)
405-
.unwrap_or_default();
406-
407-
let hive_role = HiveRole::MetaStore;
408-
409-
if let Some(HiveMetastoreRoleConfig {
410-
common: GenericRoleConfig {
411-
pod_disruption_budget: pdb,
412-
},
413-
listener_class,
414-
}) = hive.role_config(&hive_role)
415-
{
416-
role_configs.insert(
417-
hive_role.clone(),
418-
ValidatedRoleConfig {
419-
pdb: pdb.clone(),
420-
listener_class: listener_class.clone(),
421-
},
422-
);
423-
}
424-
425-
let mut group_configs = BTreeMap::new();
426-
for (rolegroup_name, rolegroup_config) in metastore_config.iter() {
427-
let rolegroup = hive.metastore_rolegroup_ref(rolegroup_name);
428-
429-
let merged_config = hive
430-
.merged_config(&hive_role, &rolegroup)
431-
.context(FailedToResolveResourceConfigSnafu)?;
432-
433-
group_configs.insert(
434-
rolegroup_name.clone(),
435-
ValidatedRoleGroupConfig {
436-
merged_config,
437-
product_config_properties: rolegroup_config.clone(),
438-
},
439-
);
440-
}
441-
442-
role_groups.insert(hive_role, group_configs);
443-
444-
Ok(ValidatedHiveCluster {
445-
image: dereferenced.resolved_product_image.clone(),
446-
role_groups,
447-
role_configs,
448-
})
449-
}
450-
451328
pub async fn reconcile_hive(
452329
hive: Arc<DeserializeGuard<v1alpha1::HiveCluster>>,
453330
ctx: Arc<Ctx>,
@@ -471,7 +348,8 @@ pub async fn reconcile_hive(
471348
.await
472349
.context(DereferenceSnafu)?;
473350

474-
let validated = validate_cluster(hive, &dereferenced, &ctx.product_config)?;
351+
let validated = validate::validate_cluster(hive, &dereferenced, &ctx.product_config)
352+
.context(ValidateSnafu)?;
475353

476354
let mut cluster_resources = ClusterResources::new(
477355
APP_NAME,
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use std::{
2+
borrow::Cow,
3+
collections::{BTreeMap, HashMap},
4+
};
5+
6+
use product_config::{ProductConfigManager, types::PropertyNameKind};
7+
use snafu::{OptionExt, ResultExt, Snafu};
8+
use stackable_operator::{
9+
commons::product_image_selection::ResolvedProductImage,
10+
product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config},
11+
role_utils::GenericRoleConfig,
12+
};
13+
14+
use super::dereference::DereferencedObjects;
15+
use crate::crd::{
16+
HIVE_SITE_XML, HiveRole, JVM_SECURITY_PROPERTIES_FILE, MetaStoreConfig,
17+
v1alpha1::{self, HiveMetastoreRoleConfig},
18+
};
19+
20+
#[derive(Snafu, Debug)]
21+
pub enum Error {
22+
#[snafu(display("object defines no metastore role"))]
23+
NoMetaStoreRole,
24+
25+
#[snafu(display("failed to generate product config"))]
26+
GenerateProductConfig {
27+
source: stackable_operator::product_config_utils::Error,
28+
},
29+
30+
#[snafu(display("invalid product config"))]
31+
InvalidProductConfig {
32+
source: stackable_operator::product_config_utils::Error,
33+
},
34+
35+
#[snafu(display("failed to resolve and merge resource config for role and role group"))]
36+
FailedToResolveResourceConfig { source: crate::crd::Error },
37+
}
38+
39+
/// Per-role configuration extracted during validation.
40+
#[derive(Clone, Debug)]
41+
pub struct ValidatedRoleConfig {
42+
pub pdb: stackable_operator::commons::pdb::PdbConfig,
43+
pub listener_class: String,
44+
}
45+
46+
/// Per-rolegroup configuration: the merged CRD config plus the product-config properties.
47+
#[derive(Clone, Debug)]
48+
pub struct ValidatedRoleGroupConfig {
49+
pub merged_config: MetaStoreConfig,
50+
pub product_config_properties: HashMap<PropertyNameKind, BTreeMap<String, String>>,
51+
}
52+
53+
/// The validated cluster: proves that product-config validation and config merging
54+
/// succeeded for every role and role group before any resources are created.
55+
#[derive(Clone, Debug)]
56+
pub struct ValidatedHiveCluster {
57+
pub image: ResolvedProductImage,
58+
pub role_groups: BTreeMap<HiveRole, BTreeMap<String, ValidatedRoleGroupConfig>>,
59+
pub role_configs: BTreeMap<HiveRole, ValidatedRoleConfig>,
60+
}
61+
62+
pub fn validate_cluster(
63+
hive: &v1alpha1::HiveCluster,
64+
dereferenced: &DereferencedObjects,
65+
product_config_manager: &ProductConfigManager,
66+
) -> Result<ValidatedHiveCluster, Error> {
67+
let role = hive.spec.metastore.as_ref().context(NoMetaStoreRoleSnafu)?;
68+
69+
let validated_config = validate_all_roles_and_groups_config(
70+
&dereferenced.resolved_product_image.product_version,
71+
&transform_all_roles_to_config(
72+
hive,
73+
&[(
74+
HiveRole::MetaStore.to_string(),
75+
(
76+
vec![
77+
PropertyNameKind::Env,
78+
PropertyNameKind::Cli,
79+
PropertyNameKind::File(HIVE_SITE_XML.to_string()),
80+
PropertyNameKind::File(JVM_SECURITY_PROPERTIES_FILE.to_string()),
81+
],
82+
role.clone(),
83+
),
84+
)]
85+
.into(),
86+
)
87+
.context(GenerateProductConfigSnafu)?,
88+
product_config_manager,
89+
false,
90+
false,
91+
)
92+
.context(InvalidProductConfigSnafu)?;
93+
94+
let mut role_groups = BTreeMap::new();
95+
let mut role_configs = BTreeMap::new();
96+
97+
let metastore_config = validated_config
98+
.get(&HiveRole::MetaStore.to_string())
99+
.map(Cow::Borrowed)
100+
.unwrap_or_default();
101+
102+
let hive_role = HiveRole::MetaStore;
103+
104+
if let Some(HiveMetastoreRoleConfig {
105+
common: GenericRoleConfig {
106+
pod_disruption_budget: pdb,
107+
},
108+
listener_class,
109+
}) = hive.role_config(&hive_role)
110+
{
111+
role_configs.insert(
112+
hive_role.clone(),
113+
ValidatedRoleConfig {
114+
pdb: pdb.clone(),
115+
listener_class: listener_class.clone(),
116+
},
117+
);
118+
}
119+
120+
let mut group_configs = BTreeMap::new();
121+
for (rolegroup_name, rolegroup_config) in metastore_config.iter() {
122+
let rolegroup = hive.metastore_rolegroup_ref(rolegroup_name);
123+
124+
let merged_config = hive
125+
.merged_config(&hive_role, &rolegroup)
126+
.context(FailedToResolveResourceConfigSnafu)?;
127+
128+
group_configs.insert(
129+
rolegroup_name.clone(),
130+
ValidatedRoleGroupConfig {
131+
merged_config,
132+
product_config_properties: rolegroup_config.clone(),
133+
},
134+
);
135+
}
136+
137+
role_groups.insert(hive_role, group_configs);
138+
139+
Ok(ValidatedHiveCluster {
140+
image: dereferenced.resolved_product_image.clone(),
141+
role_groups,
142+
role_configs,
143+
})
144+
}

0 commit comments

Comments
 (0)