Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.

- Helm: Allow Pod `priorityClassName` to be configured ([#840]).
- Add support for `2.6.0` ([#849]).
- Add `prometheus.io/path|port|scheme` annotations to metrics service ([#855]).

### Changed

Expand All @@ -24,6 +25,7 @@ All notable changes to this project will be documented in this file.
[#840]: https://github.com/stackabletech/nifi-operator/pull/840
[#844]: https://github.com/stackabletech/nifi-operator/pull/844
[#849]: https://github.com/stackabletech/nifi-operator/pull/849
[#855]: https://github.com/stackabletech/nifi-operator/pull/855

## [25.7.0] - 2025-07-23

Expand Down
17 changes: 5 additions & 12 deletions rust/operator-binary/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,7 @@ use crate::{
build_tls_volume, check_or_generate_oidc_admin_password, check_or_generate_sensitive_key,
tls::{KEYSTORE_NIFI_CONTAINER_MOUNT, KEYSTORE_VOLUME_NAME, TRUSTSTORE_VOLUME_NAME},
},
service::{
build_rolegroup_headless_service, build_rolegroup_metrics_service, metrics_service_port,
rolegroup_headless_service_name, rolegroup_metrics_service_name,
},
service::{build_rolegroup_headless_service, build_rolegroup_metrics_service},
};

pub const NIFI_CONTROLLER_NAME: &str = "nificluster";
Expand Down Expand Up @@ -576,9 +573,7 @@ pub async fn reconcile_nifi(
&rolegroup,
role_group_service_recommended_labels,
role_group_service_selector.into(),
vec![metrics_service_port(
&resolved_product_image.product_version,
)],
&resolved_product_image.product_version,
)
.context(ServiceConfigurationSnafu)?;

Expand Down Expand Up @@ -926,7 +921,7 @@ async fn build_node_rolegroup_statefulset(

let node_address = format!(
"$POD_NAME.{service_name}.{namespace}.svc.{cluster_domain}",
service_name = rolegroup_headless_service_name(&rolegroup_ref.object_name()),
service_name = rolegroup_ref.rolegroup_headless_service_name(),
namespace = &nifi
.metadata
.namespace
Expand Down Expand Up @@ -1360,7 +1355,7 @@ async fn build_node_rolegroup_statefulset(
nifi,
KEYSTORE_VOLUME_NAME,
[
rolegroup_metrics_service_name(rolegroup_ref.object_name()),
rolegroup_ref.rolegroup_metrics_service_name(),
build_reporting_task_service_name(&nifi_cluster_name),
],
SecretFormat::TlsPkcs12,
Expand Down Expand Up @@ -1434,9 +1429,7 @@ async fn build_node_rolegroup_statefulset(
),
..LabelSelector::default()
},
service_name: Some(rolegroup_headless_service_name(
&rolegroup_ref.object_name(),
)),
service_name: Some(rolegroup_ref.rolegroup_headless_service_name()),
template: pod_template,
update_strategy: Some(StatefulSetUpdateStrategy {
type_: if rolling_update_supported {
Expand Down
52 changes: 29 additions & 23 deletions rust/operator-binary/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ use snafu::{ResultExt, Snafu};
use stackable_operator::{
builder::meta::ObjectMetaBuilder,
k8s_openapi::api::core::v1::{Service, ServicePort, ServiceSpec},
kvp::{Label, ObjectLabels},
kvp::{Annotations, Labels, ObjectLabels},
role_utils::RoleGroupRef,
};

use crate::crd::{HTTPS_PORT, HTTPS_PORT_NAME, METRICS_PORT, METRICS_PORT_NAME, v1alpha1};

const METRICS_SERVICE_SUFFIX: &str = "metrics";
const HEADLESS_SERVICE_SUFFIX: &str = "headless";

#[derive(Snafu, Debug)]
pub enum Error {
#[snafu(display("object is missing metadata to build owner reference"))]
Expand All @@ -24,11 +21,6 @@ pub enum Error {
MetadataBuild {
source: stackable_operator::builder::meta::Error,
},

#[snafu(display("failed to build Labels"))]
LabelBuild {
source: stackable_operator::kvp::LabelError,
},
}

/// The rolegroup headless [`Service`] is a service that allows direct access to the instances of a certain rolegroup
Expand All @@ -42,9 +34,7 @@ pub fn build_rolegroup_headless_service(
Ok(Service {
metadata: ObjectMetaBuilder::new()
.name_and_namespace(nifi)
.name(rolegroup_headless_service_name(
&role_group_ref.object_name(),
))
.name(role_group_ref.rolegroup_headless_service_name())
.ownerreference_from_resource(nifi, None, Some(true))
.context(ObjectMissingMetadataForOwnerRefSnafu)?
.with_recommended_labels(object_labels)
Expand All @@ -69,23 +59,24 @@ pub fn build_rolegroup_metrics_service(
role_group_ref: &RoleGroupRef<v1alpha1::NifiCluster>,
object_labels: ObjectLabels<v1alpha1::NifiCluster>,
selector: BTreeMap<String, String>,
ports: Vec<ServicePort>,
product_version: &str,
) -> Result<Service, Error> {
Ok(Service {
metadata: ObjectMetaBuilder::new()
.name_and_namespace(nifi)
.name(rolegroup_metrics_service_name(role_group_ref.object_name()))
.name(role_group_ref.rolegroup_metrics_service_name())
.ownerreference_from_resource(nifi, None, Some(true))
.context(ObjectMissingMetadataForOwnerRefSnafu)?
.with_recommended_labels(object_labels)
.context(MetadataBuildSnafu)?
.with_label(Label::try_from(("prometheus.io/scrape", "true")).context(LabelBuildSnafu)?)
.with_labels(prometheus_labels())
.with_annotations(prometheus_annotations(product_version))
.build(),
spec: Some(ServiceSpec {
// Internal communication does not need to be exposed
type_: Some("ClusterIP".to_string()),
cluster_ip: Some("None".to_string()),
ports: Some(ports),
ports: Some(vec![metrics_service_port(product_version)]),
selector: Some(selector),
publish_not_ready_addresses: Some(true),
..ServiceSpec::default()
Expand Down Expand Up @@ -124,13 +115,28 @@ pub fn metrics_service_port(product_version: &str) -> ServicePort {
}
}

/// Returns the metrics rolegroup service name `<cluster>-<role>-<rolegroup>-<METRICS_SERVICE_SUFFIX>`.
pub fn rolegroup_metrics_service_name(role_group_ref_object_name: impl AsRef<str>) -> String {
let role_group_ref_object_name = role_group_ref_object_name.as_ref();
format!("{role_group_ref_object_name}-{METRICS_SERVICE_SUFFIX}")
/// Common labels for Prometheus
fn prometheus_labels() -> Labels {
Labels::try_from([("prometheus.io/scrape", "true")]).expect("should be a valid label")
}

/// Returns the headless rolegroup service name `<cluster>-<role>-<rolegroup>-<HEADLESS_SERVICE_SUFFIX>`.
pub fn rolegroup_headless_service_name(role_group_ref_object_name: &str) -> String {
format!("{role_group_ref_object_name}-{HEADLESS_SERVICE_SUFFIX}")
/// Common annotations for Prometheus
///
/// These annotations can be used in a ServiceMonitor.
///
/// see also <https://github.com/prometheus-community/helm-charts/blob/prometheus-27.32.0/charts/prometheus/values.yaml#L983-L1036>
fn prometheus_annotations(product_version: &str) -> Annotations {
let (path, port, scheme) = if product_version.starts_with("1.") {
("/metrics", METRICS_PORT, "http")
} else {
("/nifi-api/flow/metrics/prometheus", HTTPS_PORT, "https")
};

Annotations::try_from([
("prometheus.io/path".to_owned(), path.to_owned()),
("prometheus.io/port".to_owned(), port.to_string()),
("prometheus.io/scheme".to_owned(), scheme.to_owned()),
("prometheus.io/scrape".to_owned(), "true".to_owned()),
])
.expect("should be valid annotations")
}
Loading