Skip to content

Commit 6c503b9

Browse files
committed
refactor: Add ValidatedCluster methods and use RoleGroupName.
1 parent 1acf727 commit 6c503b9

8 files changed

Lines changed: 253 additions & 183 deletions

File tree

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

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
33
use snafu::{OptionExt, ResultExt, Snafu};
44
use stackable_operator::{
5-
builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder},
5+
builder::configmap::ConfigMapBuilder,
66
k8s_openapi::api::core::v1::ConfigMap,
77
product_logging::framework::VECTOR_CONFIG_FILE,
8-
role_utils::RoleGroupRef,
9-
v2::{builder::meta::ownerreference_from_resource, config_file_writer::PropertiesWriterError},
8+
v2::{config_file_writer::PropertiesWriterError, types::operator::RoleGroupName},
109
};
1110

1211
use crate::{
@@ -17,8 +16,7 @@ use crate::{
1716
ssl_server,
1817
},
1918
},
20-
crd::{HbaseRole, v1alpha1},
21-
hbase_controller::build_recommended_labels,
19+
crd::HbaseRole,
2220
};
2321

2422
#[derive(Snafu, Debug)]
@@ -35,11 +33,6 @@ pub enum Error {
3533
role_group: String,
3634
},
3735

38-
#[snafu(display("failed to build object meta data"))]
39-
ObjectMeta {
40-
source: stackable_operator::builder::meta::Error,
41-
},
42-
4336
#[snafu(display("cannot build config map for role {role:?} and role group {role_group:?}"))]
4437
Assemble {
4538
source: stackable_operator::builder::configmap::Error,
@@ -53,17 +46,20 @@ type Result<T, E = Error> = std::result::Result<T, E>;
5346
pub fn build_rolegroup_config_map(
5447
cluster: &ValidatedCluster,
5548
role: &HbaseRole,
56-
rolegroup_ref: &RoleGroupRef<v1alpha1::HbaseCluster>,
49+
role_group_name: &RoleGroupName,
5750
) -> Result<ConfigMap> {
58-
tracing::info!("Setting up ConfigMap for {:?}", rolegroup_ref);
51+
tracing::info!(
52+
"Setting up ConfigMap for {role}/{role_group_name}",
53+
role = role.to_string()
54+
);
5955

6056
let rg = cluster
6157
.role_group_configs
6258
.get(role)
63-
.and_then(|groups| groups.get(&rolegroup_ref.role_group))
59+
.and_then(|groups| groups.get(role_group_name))
6460
.with_context(|| MissingRoleGroupSnafu {
65-
role: rolegroup_ref.role.clone(),
66-
role_group: rolegroup_ref.role_group.clone(),
61+
role: role.to_string(),
62+
role_group: role_group_name.to_string(),
6763
})?;
6864

6965
let cluster_config = &cluster.cluster_config;
@@ -101,21 +97,19 @@ pub fn build_rolegroup_config_map(
10197
let security_properties =
10298
security_properties::build(role, overrides.security_properties.clone()).with_context(
10399
|_| JvmSecurityPropertiesSnafu {
104-
role_group: rolegroup_ref.role_group.clone(),
100+
role_group: role_group_name.to_string(),
105101
},
106102
)?;
107103

108-
let cm_metadata = ObjectMetaBuilder::new()
109-
.name_and_namespace(cluster)
110-
.name(rolegroup_ref.object_name())
111-
.ownerreference(ownerreference_from_resource(cluster, None, Some(true)))
112-
.with_recommended_labels(&build_recommended_labels(
113-
cluster,
114-
&cluster.image.app_version_label_value,
115-
&rolegroup_ref.role,
116-
&rolegroup_ref.role_group,
117-
))
118-
.context(ObjectMetaSnafu)?
104+
let cm_metadata = cluster
105+
.object_meta(
106+
cluster
107+
.resource_names(role, role_group_name)
108+
.role_group_config_map()
109+
.to_string(),
110+
role,
111+
role_group_name,
112+
)
119113
.build();
120114

121115
let mut builder = ConfigMapBuilder::new();
@@ -137,12 +131,14 @@ pub fn build_rolegroup_config_map(
137131
if let Some(log4j2_properties) = logging::build_log4j2(rg.config.logging()) {
138132
builder.add_data(ConfigFileName::Log4j2.to_string(), log4j2_properties);
139133
}
140-
if let Some(vector_config) = logging::build_vector_config(rolegroup_ref, rg.config.logging()) {
134+
if let Some(vector_config) =
135+
logging::build_vector_config(cluster, role, role_group_name, rg.config.logging())
136+
{
141137
builder.add_data(VECTOR_CONFIG_FILE, vector_config);
142138
}
143139

144140
builder.build().with_context(|_| AssembleSnafu {
145-
role: rolegroup_ref.role.clone(),
146-
role_group: rolegroup_ref.role_group.clone(),
141+
role: role.to_string(),
142+
role_group: role_group_name.to_string(),
147143
})
148144
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use stackable_operator::{
2+
kube::runtime::reflector::ObjectRef,
23
memory::{BinaryMultiple, MemoryQuantity},
34
product_logging::{
45
self,
@@ -7,9 +8,13 @@ use stackable_operator::{
78
},
89
},
910
role_utils::RoleGroupRef,
11+
v2::types::operator::RoleGroupName,
1012
};
1113

12-
use crate::crd::{Container, v1alpha1};
14+
use crate::{
15+
controller::ValidatedCluster,
16+
crd::{Container, HbaseRole},
17+
};
1318

1419
pub const STACKABLE_LOG_DIR: &str = "/stackable/log";
1520
pub const MAX_HBASE_LOG_FILES_SIZE: MemoryQuantity = MemoryQuantity {
@@ -38,7 +43,9 @@ pub fn build_log4j2(logging: &Logging<Container>) -> Option<String> {
3843
///
3944
/// Returns `None` when the Vector agent is disabled for this role group.
4045
pub fn build_vector_config(
41-
rolegroup: &RoleGroupRef<v1alpha1::HbaseCluster>,
46+
cluster: &ValidatedCluster,
47+
role: &HbaseRole,
48+
role_group_name: &RoleGroupName,
4249
logging: &Logging<Container>,
4350
) -> Option<String> {
4451
if !logging.enable_vector_agent {
@@ -52,8 +59,14 @@ pub fn build_vector_config(
5259
_ => None,
5360
};
5461

62+
let rolegroup = RoleGroupRef {
63+
cluster: ObjectRef::from_obj(cluster),
64+
role: role.to_string(),
65+
role_group: role_group_name.to_string(),
66+
};
67+
5568
Some(product_logging::framework::create_vector_config(
56-
rolegroup,
69+
&rolegroup,
5770
vector_log_config,
5871
))
5972
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ mod tests {
6060

6161
#[cfg(test)]
6262
pub(crate) mod test_support {
63+
use std::str::FromStr;
64+
6365
use stackable_operator::{
6466
commons::networking::DomainName, utils::cluster_info::KubernetesClusterInfo,
6567
};
@@ -133,6 +135,9 @@ spec:
133135
validated_cluster: &'a ValidatedCluster,
134136
role: &HbaseRole,
135137
) -> &'a AnyServiceConfig {
136-
&validated_cluster.role_group_configs[role]["default"].config
138+
let default_role_group =
139+
stackable_operator::v2::types::operator::RoleGroupName::from_str("default")
140+
.expect("'default' is a valid role group name");
141+
&validated_cluster.role_group_configs[role][&default_role_group].config
137142
}
138143
}

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

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,52 @@ pub mod build;
22
pub mod dereference;
33
pub mod validate;
44

5-
use std::collections::BTreeMap;
5+
use std::{collections::BTreeMap, str::FromStr};
66

77
use stackable_operator::{
8+
builder::meta::ObjectMetaBuilder,
89
commons::product_image_selection::ResolvedProductImage,
910
k8s_openapi::{api::core::v1::PodTemplateSpec, apimachinery::pkg::apis::meta::v1::ObjectMeta},
1011
kube::Resource,
12+
kvp::Labels,
1113
v2::{
1214
HasName, HasUid, NameIsValidLabelValue,
13-
builder::pod::container::EnvVarSet,
15+
builder::{meta::ownerreference_from_resource, pod::container::EnvVarSet},
16+
kvp::label::{recommended_labels, role_group_selector},
17+
role_group_utils::ResourceNames,
1418
types::{
1519
kubernetes::{NamespaceName, Uid},
16-
operator::ClusterName,
20+
operator::{
21+
ClusterName, ControllerName, OperatorName, ProductName, ProductVersion,
22+
RoleGroupName, RoleName,
23+
},
1724
},
1825
},
1926
};
2027

2128
use crate::{
22-
crd::{AnyServiceConfig, HbaseRole, v1alpha1},
29+
crd::{APP_NAME, AnyServiceConfig, HbaseRole, OPERATOR_NAME, v1alpha1},
30+
hbase_controller::HBASE_CONTROLLER_NAME,
2331
security::opa::HbaseOpaConfig,
2432
zookeeper::ZookeeperConnectionInformation,
2533
};
2634

35+
/// The product name (`hbase`) as a type-safe label value.
36+
pub(crate) fn product_name() -> ProductName {
37+
ProductName::from_str(APP_NAME).expect("'hbase' is a valid product name")
38+
}
39+
40+
/// The operator name as a type-safe label value.
41+
pub(crate) fn operator_name() -> OperatorName {
42+
OperatorName::from_str(OPERATOR_NAME).expect("the operator name is a valid label value")
43+
}
44+
45+
/// The controller name as a type-safe label value.
46+
pub(crate) fn controller_name() -> ControllerName {
47+
ControllerName::from_str(HBASE_CONTROLLER_NAME)
48+
.expect("the controller name is a valid label value")
49+
}
50+
2751
/// The validated cluster: proves that config merging and validation succeeded for
2852
/// every role and role group before any resources are created.
2953
#[derive(Clone, Debug)]
@@ -37,8 +61,12 @@ pub struct ValidatedCluster {
3761
/// The UID of the `HbaseCluster` object, used to build owner references.
3862
pub uid: Uid,
3963
pub image: ResolvedProductImage,
64+
/// The product version as a valid label value, used for the recommended
65+
/// `app.kubernetes.io/version` label. Derived from the resolved image's app version label
66+
/// value.
67+
pub product_version: ProductVersion,
4068
pub cluster_config: ValidatedClusterConfig,
41-
pub role_group_configs: BTreeMap<HbaseRole, BTreeMap<String, ValidatedRoleGroupConfig>>,
69+
pub role_group_configs: BTreeMap<HbaseRole, BTreeMap<RoleGroupName, ValidatedRoleGroupConfig>>,
4270
pub role_configs: BTreeMap<HbaseRole, ValidatedRoleConfig>,
4371
}
4472

@@ -50,9 +78,13 @@ impl ValidatedCluster {
5078
uid: Uid,
5179
image: ResolvedProductImage,
5280
cluster_config: ValidatedClusterConfig,
53-
role_group_configs: BTreeMap<HbaseRole, BTreeMap<String, ValidatedRoleGroupConfig>>,
81+
role_group_configs: BTreeMap<HbaseRole, BTreeMap<RoleGroupName, ValidatedRoleGroupConfig>>,
5482
role_configs: BTreeMap<HbaseRole, ValidatedRoleConfig>,
5583
) -> Self {
84+
// `app_version_label_value` is constructed to be a valid label value, so it is also a
85+
// valid `ProductVersion`.
86+
let product_version = ProductVersion::from_str(&image.app_version_label_value)
87+
.expect("the app version label value is a valid product version");
5688
Self {
5789
metadata: ObjectMeta {
5890
name: Some(name.to_string()),
@@ -63,11 +95,90 @@ impl ValidatedCluster {
6395
name,
6496
uid,
6597
image,
98+
product_version,
6699
cluster_config,
67100
role_group_configs,
68101
role_configs,
69102
}
70103
}
104+
105+
/// The Kubernetes role name for an [`HbaseRole`] (e.g. `master`, `regionserver`,
106+
/// `restserver`).
107+
pub fn role_name(hbase_role: &HbaseRole) -> RoleName {
108+
RoleName::from_str(&hbase_role.to_string()).expect("an HbaseRole name is a valid role name")
109+
}
110+
111+
/// Type-safe names for the resources of a given role group.
112+
pub(crate) fn resource_names(
113+
&self,
114+
hbase_role: &HbaseRole,
115+
role_group_name: &RoleGroupName,
116+
) -> ResourceNames {
117+
ResourceNames {
118+
cluster_name: self.name.clone(),
119+
role_name: Self::role_name(hbase_role),
120+
role_group_name: role_group_name.clone(),
121+
}
122+
}
123+
124+
/// Recommended labels for a role-group resource.
125+
pub fn recommended_labels(
126+
&self,
127+
hbase_role: &HbaseRole,
128+
role_group_name: &RoleGroupName,
129+
) -> Labels {
130+
recommended_labels(
131+
self,
132+
&product_name(),
133+
&self.product_version,
134+
&operator_name(),
135+
&controller_name(),
136+
&Self::role_name(hbase_role),
137+
role_group_name,
138+
)
139+
}
140+
141+
/// Selector labels matching the pods of a role group.
142+
pub fn role_group_selector(
143+
&self,
144+
hbase_role: &HbaseRole,
145+
role_group_name: &RoleGroupName,
146+
) -> Labels {
147+
role_group_selector(
148+
self,
149+
&product_name(),
150+
&Self::role_name(hbase_role),
151+
role_group_name,
152+
)
153+
}
154+
155+
/// Returns an [`ObjectMetaBuilder`] pre-filled with the namespace, an owner reference back to
156+
/// this cluster, and the recommended labels for a resource named `name` in `role_group_name`.
157+
///
158+
/// Consolidates the metadata chain repeated by the child-resource builders. Call sites that
159+
/// need extra labels/annotations chain them onto the returned builder.
160+
pub(crate) fn object_meta(
161+
&self,
162+
name: impl Into<String>,
163+
hbase_role: &HbaseRole,
164+
role_group_name: &RoleGroupName,
165+
) -> ObjectMetaBuilder {
166+
let mut builder = ObjectMetaBuilder::new();
167+
builder
168+
.name_and_namespace(self)
169+
.name(name)
170+
.ownerreference(ownerreference_from_resource(self, None, Some(true)))
171+
.with_labels(self.recommended_labels(hbase_role, role_group_name));
172+
builder
173+
}
174+
175+
/// Whether Kerberos is enabled for this cluster.
176+
///
177+
/// Mirrors [`v1alpha1::HbaseCluster::has_kerberos_enabled`], derived here from the validated
178+
/// config so build steps don't have to re-read the raw cluster.
179+
pub fn has_kerberos_enabled(&self) -> bool {
180+
self.cluster_config.kerberos_enabled
181+
}
71182
}
72183

73184
impl Resource for ValidatedCluster {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use stackable_operator::{
1414
builder::pod::container::{self, EnvVarName, EnvVarSet},
1515
controller_utils::{get_cluster_name, get_namespace, get_uid},
1616
role_utils::{JavaCommonConfig, with_validated_config},
17+
types::operator::RoleGroupName,
1718
},
1819
};
1920
use strum::IntoEnumIterator;
@@ -53,6 +54,12 @@ pub enum Error {
5354
#[snafu(display("invalid environment variable override name"))]
5455
ParseEnvVarName { source: container::Error },
5556

57+
#[snafu(display("invalid role group name {role_group}"))]
58+
ParseRoleGroupName {
59+
source: stackable_operator::v2::macros::attributed_string_type::Error,
60+
role_group: String,
61+
},
62+
5663
#[snafu(display("failed to resolve kerberos config"))]
5764
AddKerberosConfig { source: kerberos::Error },
5865
}
@@ -187,7 +194,7 @@ fn validate_role_group_configs<Config, ValidatedConfig>(
187194
>,
188195
default_config: Config,
189196
wrap: fn(ValidatedConfig) -> AnyServiceConfig,
190-
) -> Result<BTreeMap<String, ValidatedRoleGroupConfig>, Error>
197+
) -> Result<BTreeMap<RoleGroupName, ValidatedRoleGroupConfig>, Error>
191198
where
192199
Config: Clone + Merge,
193200
ValidatedConfig: FromFragment<Fragment = Config>,
@@ -199,6 +206,11 @@ where
199206
role.role_groups
200207
.iter()
201208
.map(|(role_group_name, role_group)| {
209+
let role_group_name = RoleGroupName::from_str(role_group_name).with_context(|_| {
210+
ParseRoleGroupNameSnafu {
211+
role_group: role_group_name.clone(),
212+
}
213+
})?;
202214
let validated = with_validated_config::<
203215
ValidatedConfig,
204216
JavaCommonConfig,

0 commit comments

Comments
 (0)