Skip to content

Commit 06cb1ff

Browse files
authored
Listener: Use headless and metrics service (#766)
* wip - split metrics and headless service * adapt listener test * add listener svc name to cert, use headless svc in sts * fix remaining tests * fix cargo doc * use listener scope for server tls cert * remove listener related comments * remove headless service from worker * adapted changelog * revert: remove headless worker service * improve string formatting
1 parent 129caf5 commit 06cb1ff

File tree

11 files changed

+227
-83
lines changed

11 files changed

+227
-83
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file.
1212
- Use `--console-log-format` (or `CONSOLE_LOG_FORMAT`) to set the format to `plain` (default) or `json`.
1313
- Add Listener integration for Trino ([#753]).
1414
- Add support for Trino 476 ([#755]).
15+
- Add internal headless service in addition to the metrics service ([#766]).
1516

1617
### Changed
1718

@@ -56,6 +57,7 @@ All notable changes to this project will be documented in this file.
5657
[#753]: https://github.com/stackabletech/trino-operator/pull/753
5758
[#755]: https://github.com/stackabletech/trino-operator/pull/755
5859
[#760]: https://github.com/stackabletech/trino-operator/pull/760
60+
[#766]: https://github.com/stackabletech/trino-operator/pull/766
5961

6062
## [25.3.0] - 2025-03-21
6163

rust/operator-binary/src/controller.rs

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ use stackable_operator::{
3636
apps::v1::{StatefulSet, StatefulSetSpec},
3737
core::v1::{
3838
ConfigMap, ConfigMapVolumeSource, ContainerPort, EnvVar, EnvVarSource, ExecAction,
39-
HTTPGetAction, Probe, Secret, SecretKeySelector, Service, ServicePort, ServiceSpec,
40-
Volume,
39+
HTTPGetAction, Probe, Secret, SecretKeySelector, Volume,
4140
},
4241
},
4342
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
@@ -47,7 +46,7 @@ use stackable_operator::{
4746
core::{DeserializeGuard, error_boundary},
4847
runtime::{controller::Action, reflector::ObjectRef},
4948
},
50-
kvp::{Annotation, Label, Labels, ObjectLabels},
49+
kvp::{Annotation, Labels, ObjectLabels},
5150
logging::controller::ReconcilerError,
5251
memory::{BinaryMultiple, MemoryQuantity},
5352
product_config_utils::{
@@ -88,16 +87,17 @@ use crate::{
8887
authentication::resolve_authentication_classes,
8988
catalog,
9089
discovery::{TrinoDiscovery, TrinoDiscoveryProtocol, TrinoPodRef},
91-
rolegroup_metrics_service_name, v1alpha1,
90+
rolegroup_headless_service_name, v1alpha1,
9291
},
9392
listener::{
9493
LISTENER_VOLUME_DIR, LISTENER_VOLUME_NAME, build_group_listener, build_group_listener_pvc,
95-
group_listener_name,
94+
group_listener_name, secret_volume_listener_scope,
9695
},
9796
operations::{
9897
add_graceful_shutdown_config, graceful_shutdown_config_properties, pdb::add_pdbs,
9998
},
10099
product_logging::{get_log_properties, get_vector_toml},
100+
service::{build_rolegroup_headless_service, build_rolegroup_metrics_service},
101101
};
102102

103103
pub struct Ctx {
@@ -357,6 +357,9 @@ pub enum Error {
357357

358358
#[snafu(display("failed to configure listener"))]
359359
ListenerConfiguration { source: crate::listener::Error },
360+
361+
#[snafu(display("failed to configure service"))]
362+
ServiceConfiguration { source: crate::service::Error },
360363
}
361364

362365
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -482,8 +485,37 @@ pub async fn reconcile_trino(
482485
.merged_config(&trino_role, &role_group_ref, &catalog_definitions)
483486
.context(FailedToResolveConfigSnafu)?;
484487

485-
let rg_service =
486-
build_rolegroup_service(trino, &resolved_product_image, &role_group_ref)?;
488+
let role_group_service_recommended_labels = build_recommended_labels(
489+
trino,
490+
&resolved_product_image.app_version_label,
491+
&role_group_ref.role,
492+
&role_group_ref.role_group,
493+
);
494+
495+
let role_group_service_selector = Labels::role_group_selector(
496+
trino,
497+
APP_NAME,
498+
&role_group_ref.role,
499+
&role_group_ref.role_group,
500+
)
501+
.context(LabelBuildSnafu)?;
502+
503+
let rg_headless_service = build_rolegroup_headless_service(
504+
trino,
505+
&role_group_ref,
506+
role_group_service_recommended_labels.clone(),
507+
role_group_service_selector.clone().into(),
508+
)
509+
.context(ServiceConfigurationSnafu)?;
510+
511+
let rg_metrics_service = build_rolegroup_metrics_service(
512+
trino,
513+
&role_group_ref,
514+
role_group_service_recommended_labels,
515+
role_group_service_selector.into(),
516+
)
517+
.context(ServiceConfigurationSnafu)?;
518+
487519
let rg_configmap = build_rolegroup_config_map(
488520
trino,
489521
&resolved_product_image,
@@ -515,7 +547,14 @@ pub async fn reconcile_trino(
515547
)?;
516548

517549
cluster_resources
518-
.add(client, rg_service)
550+
.add(client, rg_headless_service)
551+
.await
552+
.with_context(|_| ApplyRoleGroupServiceSnafu {
553+
rolegroup: role_group_ref.clone(),
554+
})?;
555+
556+
cluster_resources
557+
.add(client, rg_metrics_service)
519558
.await
520559
.with_context(|_| ApplyRoleGroupServiceSnafu {
521560
rolegroup: role_group_ref.clone(),
@@ -834,7 +873,7 @@ fn build_rolegroup_catalog_config_map(
834873
/// The rolegroup [`StatefulSet`] runs the rolegroup, as configured by the administrator.
835874
///
836875
/// The [`Pod`](`stackable_operator::k8s_openapi::api::core::v1::Pod`)s are accessible through the
837-
/// corresponding [`Service`] (from [`build_rolegroup_service`]).
876+
/// corresponding [`stackable_operator::k8s_openapi::api::core::v1::Service`] (from [`build_rolegroup_headless_service`]).
838877
#[allow(clippy::too_many_arguments)]
839878
fn build_rolegroup_statefulset(
840879
trino: &v1alpha1::TrinoCluster,
@@ -930,6 +969,7 @@ fn build_rolegroup_statefulset(
930969
// add volume mounts depending on the client tls, internal tls, catalogs and authentication
931970
tls_volume_mounts(
932971
trino,
972+
trino_role,
933973
&mut pod_builder,
934974
&mut cb_prepare,
935975
&mut cb_trino,
@@ -1193,7 +1233,7 @@ fn build_rolegroup_statefulset(
11931233
),
11941234
..LabelSelector::default()
11951235
},
1196-
service_name: Some(rolegroup_metrics_service_name(
1236+
service_name: Some(rolegroup_headless_service_name(
11971237
&role_group_ref.object_name(),
11981238
)),
11991239
template: pod_template,
@@ -1204,53 +1244,6 @@ fn build_rolegroup_statefulset(
12041244
})
12051245
}
12061246

1207-
/// The rolegroup [`Service`] is a headless service that allows direct access to the instances of a certain rolegroup
1208-
///
1209-
/// This is mostly useful for internal communication between peers, or for clients that perform client-side load balancing.
1210-
fn build_rolegroup_service(
1211-
trino: &v1alpha1::TrinoCluster,
1212-
resolved_product_image: &ResolvedProductImage,
1213-
role_group_ref: &RoleGroupRef<v1alpha1::TrinoCluster>,
1214-
) -> Result<Service> {
1215-
Ok(Service {
1216-
metadata: ObjectMetaBuilder::new()
1217-
.name_and_namespace(trino)
1218-
.name(rolegroup_metrics_service_name(
1219-
&role_group_ref.object_name(),
1220-
))
1221-
.ownerreference_from_resource(trino, None, Some(true))
1222-
.context(ObjectMissingMetadataForOwnerRefSnafu)?
1223-
.with_recommended_labels(build_recommended_labels(
1224-
trino,
1225-
&resolved_product_image.app_version_label,
1226-
&role_group_ref.role,
1227-
&role_group_ref.role_group,
1228-
))
1229-
.context(MetadataBuildSnafu)?
1230-
.with_label(Label::try_from(("prometheus.io/scrape", "true")).context(LabelBuildSnafu)?)
1231-
.build(),
1232-
spec: Some(ServiceSpec {
1233-
// Internal communication does not need to be exposed
1234-
type_: Some("ClusterIP".to_string()),
1235-
cluster_ip: Some("None".to_string()),
1236-
ports: Some(service_ports()),
1237-
selector: Some(
1238-
Labels::role_group_selector(
1239-
trino,
1240-
APP_NAME,
1241-
&role_group_ref.role,
1242-
&role_group_ref.role_group,
1243-
)
1244-
.context(LabelBuildSnafu)?
1245-
.into(),
1246-
),
1247-
publish_not_ready_addresses: Some(true),
1248-
..ServiceSpec::default()
1249-
}),
1250-
status: None,
1251-
})
1252-
}
1253-
12541247
pub fn error_policy(
12551248
_obj: Arc<DeserializeGuard<v1alpha1::TrinoCluster>>,
12561249
error: &Error,
@@ -1409,15 +1402,6 @@ fn get_random_base64() -> String {
14091402
openssl::base64::encode_block(&buf)
14101403
}
14111404

1412-
fn service_ports() -> Vec<ServicePort> {
1413-
vec![ServicePort {
1414-
name: Some(METRICS_PORT_NAME.to_string()),
1415-
port: METRICS_PORT.into(),
1416-
protocol: Some("TCP".to_string()),
1417-
..ServicePort::default()
1418-
}]
1419-
}
1420-
14211405
fn container_ports(trino: &v1alpha1::TrinoCluster) -> Vec<ContainerPort> {
14221406
let mut ports = vec![ContainerPort {
14231407
name: Some(METRICS_PORT_NAME.to_string()),
@@ -1530,14 +1514,22 @@ fn create_tls_volume(
15301514
volume_name: &str,
15311515
tls_secret_class: &str,
15321516
requested_secret_lifetime: &Duration,
1517+
listener_scope: Option<String>,
15331518
) -> Result<Volume> {
1519+
let mut secret_volume_source_builder = SecretOperatorVolumeSourceBuilder::new(tls_secret_class);
1520+
1521+
secret_volume_source_builder
1522+
.with_pod_scope()
1523+
.with_format(SecretFormat::TlsPkcs12)
1524+
.with_auto_tls_cert_lifetime(*requested_secret_lifetime);
1525+
1526+
if let Some(listener_scope) = &listener_scope {
1527+
secret_volume_source_builder.with_listener_volume_scope(listener_scope);
1528+
}
1529+
15341530
Ok(VolumeBuilder::new(volume_name)
15351531
.ephemeral(
1536-
SecretOperatorVolumeSourceBuilder::new(tls_secret_class)
1537-
.with_pod_scope()
1538-
.with_node_scope()
1539-
.with_format(SecretFormat::TlsPkcs12)
1540-
.with_auto_tls_cert_lifetime(*requested_secret_lifetime)
1532+
secret_volume_source_builder
15411533
.build()
15421534
.context(TlsCertSecretClassVolumeBuildSnafu)?,
15431535
)
@@ -1546,6 +1538,7 @@ fn create_tls_volume(
15461538

15471539
fn tls_volume_mounts(
15481540
trino: &v1alpha1::TrinoCluster,
1541+
trino_role: &TrinoRole,
15491542
pod_builder: &mut PodBuilder,
15501543
cb_prepare: &mut ContainerBuilder,
15511544
cb_trino: &mut ContainerBuilder,
@@ -1564,6 +1557,8 @@ fn tls_volume_mounts(
15641557
"server-tls-mount",
15651558
server_tls,
15661559
requested_secret_lifetime,
1560+
// add listener
1561+
secret_volume_listener_scope(trino_role),
15671562
)?)
15681563
.context(AddVolumeSnafu)?;
15691564
}
@@ -1600,6 +1595,7 @@ fn tls_volume_mounts(
16001595
"internal-tls-mount",
16011596
internal_tls,
16021597
requested_secret_lifetime,
1598+
None,
16031599
)?)
16041600
.context(AddVolumeSnafu)?;
16051601

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub const MAX_TRINO_LOG_FILES_SIZE: MemoryQuantity = MemoryQuantity {
119119
};
120120

121121
pub const METRICS_SERVICE_SUFFIX: &str = "metrics";
122+
pub const HEADLESS_SERVICE_SUFFIX: &str = "headless";
122123

123124
pub const JVM_HEAP_FACTOR: f32 = 0.8;
124125

@@ -837,7 +838,7 @@ impl v1alpha1::TrinoCluster {
837838
let ns = ns.clone();
838839
(0..rolegroup.replicas.unwrap_or(0)).map(move |i| TrinoPodRef {
839840
namespace: ns.clone(),
840-
role_group_service_name: rolegroup_metrics_service_name(
841+
role_group_service_name: rolegroup_headless_service_name(
841842
&role_group_ref.object_name(),
842843
),
843844
pod_name: format!(
@@ -945,6 +946,11 @@ pub fn rolegroup_metrics_service_name(role_group_ref_object_name: &str) -> Strin
945946
format!("{role_group_ref_object_name}-{METRICS_SERVICE_SUFFIX}")
946947
}
947948

949+
/// Returns the headless rolegroup service name `<cluster>-<role>-<rolegroup>-<HEADLESS_SERVICE_SUFFIX>`.
950+
pub fn rolegroup_headless_service_name(role_group_ref_object_name: &str) -> String {
951+
format!("{role_group_ref_object_name}-{HEADLESS_SERVICE_SUFFIX}")
952+
}
953+
948954
fn extract_role_from_coordinator_config(
949955
fragment: Role<TrinoConfigFragment, TrinoCoordinatorRoleConfig, JavaCommonConfig>,
950956
) -> Role<TrinoConfigFragment, GenericRoleConfig, JavaCommonConfig> {

rust/operator-binary/src/listener.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ pub fn group_listener_name(trino: &v1alpha1::TrinoCluster, role: &TrinoRole) ->
8383
}
8484
}
8585

86+
/// The listener volume name depending on the role
87+
pub fn secret_volume_listener_scope(role: &TrinoRole) -> Option<String> {
88+
match role {
89+
TrinoRole::Coordinator => Some(LISTENER_VOLUME_NAME.to_string()),
90+
TrinoRole::Worker => None,
91+
}
92+
}
93+
8694
/// We only use the http/https port here and intentionally omit the metrics one.
8795
fn listener_ports(trino: &v1alpha1::TrinoCluster) -> Vec<ListenerPort> {
8896
let name = trino.exposed_protocol().to_string();

rust/operator-binary/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod crd;
88
mod listener;
99
mod operations;
1010
mod product_logging;
11+
mod service;
1112

1213
use std::sync::Arc;
1314

0 commit comments

Comments
 (0)