Skip to content

Commit f5b2e16

Browse files
committed
replace role_name with From impls, add missing labels
1 parent 5198088 commit f5b2e16

5 files changed

Lines changed: 91 additions & 12 deletions

File tree

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,61 @@ mod tests {
365365
names
366366
}
367367

368+
/// Every metrics Service must carry the Prometheus scrape label and the
369+
/// `prometheus.io/path|port|scheme|scrape` annotations, or Prometheus stops discovering the
370+
/// endpoints (caught by the HDFS smoke test 2026-07-23 after the labels migration dropped
371+
/// them).
372+
#[test]
373+
fn metrics_services_carry_prometheus_label_and_annotations() {
374+
let cluster = validated_cluster();
375+
let resources = build(&cluster, &cluster_info()).expect("build succeeds");
376+
377+
let metrics_services: Vec<_> = resources
378+
.services
379+
.iter()
380+
.filter(|service| {
381+
service
382+
.metadata
383+
.name
384+
.as_deref()
385+
.is_some_and(|name| name.ends_with("-metrics"))
386+
})
387+
.collect();
388+
assert!(!metrics_services.is_empty(), "no metrics Services built");
389+
390+
for service in metrics_services {
391+
let name = service.metadata.name.as_deref().unwrap_or_default();
392+
let labels = service.metadata.labels.as_ref().expect("labels are set");
393+
assert_eq!(
394+
labels.get("prometheus.io/scrape").map(String::as_str),
395+
Some("true"),
396+
"{name} lacks the scrape label"
397+
);
398+
399+
// The native metrics port of the role, as asserted by the smoke test.
400+
let expected_port = match name {
401+
n if n.contains("-namenode-") => "9870",
402+
n if n.contains("-datanode-") => "9864",
403+
n if n.contains("-journalnode-") => "8480",
404+
other => panic!("unexpected metrics Service {other}"),
405+
};
406+
let expected_annotations = BTreeMap::from(
407+
[
408+
("prometheus.io/path", "/prom"),
409+
("prometheus.io/port", expected_port),
410+
("prometheus.io/scheme", "http"),
411+
("prometheus.io/scrape", "true"),
412+
]
413+
.map(|(key, value)| (key.to_string(), value.to_string())),
414+
);
415+
assert_eq!(
416+
service.metadata.annotations.as_ref(),
417+
Some(&expected_annotations),
418+
"{name} annotations mismatch"
419+
);
420+
}
421+
}
422+
368423
/// The aggregator emits, for the minimal three-role cluster (one `default` role group each):
369424
/// one StatefulSet and one ConfigMap per role group, one headless plus one metrics Service per
370425
/// role group, and one default PDB per role.

rust/operator-binary/src/controller/build/resource/discovery.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub fn build_discovery_config_map(
6363
&operator_name(),
6464
&ControllerName::from_str(HDFS_CONTROLLER_NAME)
6565
.expect("the hdfs controller name is a valid label value"),
66-
&ValidatedCluster::role_name(&HdfsNodeRole::Name),
66+
&HdfsNodeRole::Name.into(),
6767
&DISCOVERY_ROLE_GROUP,
6868
);
6969

rust/operator-binary/src/controller/build/resource/service.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use snafu::{ResultExt, Snafu};
55
use stackable_operator::{
66
k8s_openapi::api::core::v1::{Service, ServicePort, ServiceSpec},
77
kvp::LabelError,
8-
v2::types::operator::RoleGroupName,
8+
v2::{
9+
builder::service::{Scheme, Scraping, prometheus_annotations, prometheus_labels},
10+
types::operator::RoleGroupName,
11+
},
912
};
1013

1114
use crate::{
@@ -111,6 +114,17 @@ pub(crate) fn rolegroup_metrics_service(
111114
.to_string(),
112115
cluster.recommended_labels(role, role_group_name),
113116
)
117+
.with_labels(prometheus_labels(&Scraping::Enabled))
118+
.with_annotations(prometheus_annotations(
119+
&Scraping::Enabled,
120+
if cluster.has_https_enabled() {
121+
&Scheme::Https
122+
} else {
123+
&Scheme::Http
124+
},
125+
"/prom",
126+
&build::native_metrics_port(cluster, role),
127+
))
114128
.build(),
115129
spec: Some(service_spec),
116130
status: None,

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

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,6 @@ impl ValidatedCluster {
149149
}
150150

151151
/// The type-safe role name for an HDFS role (`namenode`/`datanode`/`journalnode`).
152-
pub(crate) fn role_name(role: &HdfsNodeRole) -> RoleName {
153-
RoleName::from_str(&role.to_string()).expect("a HdfsNodeRole is a valid role name")
154-
}
155-
156152
/// Type-safe names for the per-cluster RBAC resources: the ServiceAccount shared by all
157153
/// Pods, its (namespaced) RoleBinding, and the operator-deployed ClusterRole it binds.
158154
pub fn cluster_resource_names(&self) -> role_utils::ResourceNames {
@@ -170,7 +166,7 @@ impl ValidatedCluster {
170166
) -> ResourceNames {
171167
ResourceNames {
172168
cluster_name: self.name.clone(),
173-
role_name: Self::role_name(role),
169+
role_name: role.into(),
174170
role_group_name: role_group_name.clone(),
175171
}
176172
}
@@ -207,7 +203,7 @@ impl ValidatedCluster {
207203
role: &HdfsNodeRole,
208204
role_group_name: &RoleGroupName,
209205
) -> Labels {
210-
self.recommended_labels_for(&Self::role_name(role), role_group_name)
206+
self.recommended_labels_for(&role.into(), role_group_name)
211207
}
212208

213209
/// Returns an [`ObjectMetaBuilder`] pre-filled with the namespace, the resource `name`, an owner
@@ -382,17 +378,18 @@ pub struct ValidatedRoleConfig {
382378

383379
#[cfg(test)]
384380
mod tests {
381+
use stackable_operator::v2::types::operator::RoleName;
385382
use strum::IntoEnumIterator;
386383

387-
use super::ValidatedCluster;
388384
use crate::crd::HdfsNodeRole;
389385

390-
/// Locks the invariant behind the `expect` in [`ValidatedCluster::role_name`]: every
391-
/// `HdfsNodeRole` variant (present and future) must serialise to a valid `RoleName`.
386+
/// Locks the invariant behind the `expect` in the `From<HdfsNodeRole> for RoleName` impls:
387+
/// every `HdfsNodeRole` variant (present and future) must serialise to a valid `RoleName`.
392388
#[test]
393389
fn every_hdfs_node_role_serialises_to_a_valid_role_name() {
394390
for role in HdfsNodeRole::iter() {
395-
ValidatedCluster::role_name(&role);
391+
let _: RoleName = (&role).into();
392+
let _: RoleName = role.into();
396393
}
397394
}
398395
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use stackable_operator::{
4343
types::{
4444
common::Port,
4545
kubernetes::{ConfigMapName, ListenerClassName, NamespaceName, ServiceName},
46+
operator::RoleName,
4647
},
4748
},
4849
versioned::versioned,
@@ -497,6 +498,18 @@ pub enum HdfsNodeRole {
497498
Data,
498499
}
499500

501+
impl From<HdfsNodeRole> for RoleName {
502+
fn from(value: HdfsNodeRole) -> Self {
503+
RoleName::from_str(&value.to_string()).expect("a HdfsNodeRole is a valid role name")
504+
}
505+
}
506+
507+
impl From<&HdfsNodeRole> for RoleName {
508+
fn from(value: &HdfsNodeRole) -> Self {
509+
RoleName::from_str(&value.to_string()).expect("a HdfsNodeRole is a valid role name")
510+
}
511+
}
512+
500513
impl HdfsNodeRole {
501514
pub fn min_replicas(&self) -> u16 {
502515
match self {

0 commit comments

Comments
 (0)