Skip to content

Commit a6c579a

Browse files
committed
Moving services into own module
1 parent a668504 commit a6c579a

File tree

4 files changed

+186
-137
lines changed

4 files changed

+186
-137
lines changed

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

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use stackable_operator::{
1919
fragment::{self, Fragment, ValidationError},
2020
merge::Merge,
2121
},
22-
k8s_openapi::{api::core::v1::ServicePort, apimachinery::pkg::api::resource::Quantity},
22+
k8s_openapi::apimachinery::pkg::api::resource::Quantity,
2323
kube::{CustomResource, ResourceExt, runtime::reflector::ObjectRef},
2424
memory::{BinaryMultiple, MemoryQuantity},
2525
product_config_utils::{self, Configuration},
@@ -524,43 +524,6 @@ impl v1alpha1::SupersetCluster {
524524
}
525525
}
526526

527-
/// Set of functions to define service names on rolegroup level.
528-
/// Headless service for cluster internal purposes only.
529-
// TODO: Move to operator-rs
530-
pub fn rolegroup_headless_service_name(
531-
&self,
532-
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
533-
) -> String {
534-
format!("{name}-headless", name = rolegroup.object_name())
535-
}
536-
537-
/// Headless metrics service exposes Prometheus endpoint only
538-
// TODO: Move to operator-rs
539-
pub fn rolegroup_headless_metrics_service_name(
540-
&self,
541-
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
542-
) -> String {
543-
format!("{name}-metrics", name = rolegroup.object_name())
544-
}
545-
546-
pub fn metrics_ports(&self) -> Vec<ServicePort> {
547-
vec![ServicePort {
548-
name: Some(METRICS_PORT_NAME.to_string()),
549-
port: METRICS_PORT.into(),
550-
protocol: Some("TCP".to_string()),
551-
..ServicePort::default()
552-
}]
553-
}
554-
555-
pub fn service_ports(&self) -> Vec<ServicePort> {
556-
vec![ServicePort {
557-
name: Some(APP_PORT_NAME.to_string()),
558-
port: APP_PORT.into(),
559-
protocol: Some("TCP".to_string()),
560-
..ServicePort::default()
561-
}]
562-
}
563-
564527
pub fn generic_role_config(&self, role: &SupersetRole) -> Option<GenericRoleConfig> {
565528
self.get_role_config(role).map(|r| r.common.to_owned())
566529
}

rust/operator-binary/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ mod listener;
4646
mod operations;
4747
mod product_logging;
4848
mod rbac;
49+
mod service;
4950
mod superset_controller;
5051
mod util;
5152

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use snafu::{ResultExt, Snafu};
2+
use stackable_operator::{
3+
builder::meta::ObjectMetaBuilder,
4+
commons::product_image_selection::ResolvedProductImage,
5+
k8s_openapi::api::core::v1::{Service, ServicePort, ServiceSpec},
6+
kvp::{Label, Labels},
7+
role_utils::RoleGroupRef,
8+
};
9+
10+
use crate::{
11+
crd::{APP_NAME, APP_PORT, APP_PORT_NAME, METRICS_PORT, METRICS_PORT_NAME, v1alpha1},
12+
superset_controller::SUPERSET_CONTROLLER_NAME,
13+
util::build_recommended_labels,
14+
};
15+
#[derive(Debug, Snafu)]
16+
pub enum Error {
17+
#[snafu(display("object is missing metadata to build owner reference"))]
18+
ObjectMissingMetadataForOwnerRef {
19+
source: stackable_operator::builder::meta::Error,
20+
},
21+
#[snafu(display("failed to build Metadata"))]
22+
MetadataBuild {
23+
source: stackable_operator::builder::meta::Error,
24+
},
25+
#[snafu(display("failed to build Labels"))]
26+
LabelBuild {
27+
source: stackable_operator::kvp::LabelError,
28+
},
29+
}
30+
31+
/// The rolegroup [`Service`] is a headless service that allows direct access to the instances of a certain rolegroup
32+
///
33+
/// This is mostly useful for internal communication between peers, or for clients that perform client-side load balancing.
34+
pub fn build_node_rolegroup_headless_service(
35+
superset: &v1alpha1::SupersetCluster,
36+
resolved_product_image: &ResolvedProductImage,
37+
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
38+
) -> Result<Service, Error> {
39+
let headless_service = Service {
40+
metadata: ObjectMetaBuilder::new()
41+
.name_and_namespace(superset)
42+
.name(rolegroup_headless_service_name(rolegroup))
43+
.ownerreference_from_resource(superset, None, Some(true))
44+
.context(ObjectMissingMetadataForOwnerRefSnafu)?
45+
.with_recommended_labels(build_recommended_labels(
46+
superset,
47+
SUPERSET_CONTROLLER_NAME,
48+
&resolved_product_image.app_version_label,
49+
&rolegroup.role,
50+
&rolegroup.role_group,
51+
))
52+
.context(MetadataBuildSnafu)?
53+
.build(),
54+
spec: Some(ServiceSpec {
55+
// Internal communication does not need to be exposed
56+
type_: Some("ClusterIP".to_owned()),
57+
cluster_ip: Some("None".to_owned()),
58+
ports: Some(service_ports()),
59+
selector: Some(
60+
Labels::role_group_selector(
61+
superset,
62+
APP_NAME,
63+
&rolegroup.role,
64+
&rolegroup.role_group,
65+
)
66+
.context(LabelBuildSnafu)?
67+
.into(),
68+
),
69+
publish_not_ready_addresses: Some(true),
70+
..ServiceSpec::default()
71+
}),
72+
status: None,
73+
};
74+
Ok(headless_service)
75+
}
76+
77+
/// The rolegroup metrics [`Service`] is a service that exposes metrics and a prometheus scraping label
78+
pub fn build_node_rolegroup_metrics_service(
79+
superset: &v1alpha1::SupersetCluster,
80+
resolved_product_image: &ResolvedProductImage,
81+
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
82+
) -> Result<Service, Error> {
83+
let metrics_service = Service {
84+
metadata: ObjectMetaBuilder::new()
85+
.name_and_namespace(superset)
86+
.name(rolegroup_headless_metrics_service_name(rolegroup))
87+
.ownerreference_from_resource(superset, None, Some(true))
88+
.context(ObjectMissingMetadataForOwnerRefSnafu)?
89+
.with_recommended_labels(build_recommended_labels(
90+
superset,
91+
SUPERSET_CONTROLLER_NAME,
92+
&resolved_product_image.app_version_label,
93+
&rolegroup.role,
94+
&rolegroup.role_group,
95+
))
96+
.context(MetadataBuildSnafu)?
97+
.with_label(Label::try_from(("prometheus.io/scrape", "true")).context(LabelBuildSnafu)?)
98+
.build(),
99+
spec: Some(ServiceSpec {
100+
// Internal communication does not need to be exposed
101+
type_: Some("ClusterIP".to_owned()),
102+
cluster_ip: Some("None".to_owned()),
103+
ports: Some(metrics_ports()),
104+
selector: Some(
105+
Labels::role_group_selector(
106+
superset,
107+
APP_NAME,
108+
&rolegroup.role,
109+
&rolegroup.role_group,
110+
)
111+
.context(LabelBuildSnafu)?
112+
.into(),
113+
),
114+
publish_not_ready_addresses: Some(true),
115+
..ServiceSpec::default()
116+
}),
117+
status: None,
118+
};
119+
120+
Ok(metrics_service)
121+
}
122+
123+
/// Headless service for cluster internal purposes only.
124+
// TODO: Move to operator-rs
125+
pub fn rolegroup_headless_service_name(
126+
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
127+
) -> String {
128+
format!("{name}-headless", name = rolegroup.object_name())
129+
}
130+
131+
/// Headless metrics service exposes Prometheus endpoint only
132+
// TODO: Move to operator-rs
133+
pub fn rolegroup_headless_metrics_service_name(
134+
rolegroup: &RoleGroupRef<v1alpha1::SupersetCluster>,
135+
) -> String {
136+
format!("{name}-metrics", name = rolegroup.object_name())
137+
}
138+
139+
fn metrics_ports() -> Vec<ServicePort> {
140+
vec![ServicePort {
141+
name: Some(METRICS_PORT_NAME.to_string()),
142+
port: METRICS_PORT.into(),
143+
protocol: Some("TCP".to_string()),
144+
..ServicePort::default()
145+
}]
146+
}
147+
148+
fn service_ports() -> Vec<ServicePort> {
149+
vec![ServicePort {
150+
name: Some(APP_PORT_NAME.to_string()),
151+
port: APP_PORT.into(),
152+
protocol: Some("TCP".to_string()),
153+
..ServicePort::default()
154+
}]
155+
}

rust/operator-binary/src/superset_controller.rs

Lines changed: 29 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use stackable_operator::{
3737
DeepMerge,
3838
api::{
3939
apps::v1::{StatefulSet, StatefulSetSpec},
40-
core::v1::{ConfigMap, EnvVar, HTTPGetAction, Probe, Service, ServiceSpec},
40+
core::v1::{ConfigMap, EnvVar, HTTPGetAction, Probe},
4141
},
4242
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
4343
},
@@ -87,6 +87,7 @@ use crate::{
8787
listener::{LISTENER_VOLUME_DIR, LISTENER_VOLUME_NAME, build_group_listener},
8888
operations::{graceful_shutdown::add_graceful_shutdown_config, pdb::add_pdbs},
8989
product_logging::{LOG_CONFIG_FILE, extend_config_map_with_log_config},
90+
service::{build_node_rolegroup_headless_service, build_node_rolegroup_metrics_service},
9091
util::{build_recommended_labels, rolegroup_metrics_service_name},
9192
};
9293

@@ -299,6 +300,8 @@ pub enum Error {
299300
},
300301
#[snafu(display("failed to configure listener"))]
301302
ListenerConfiguration { source: crate::listener::Error },
303+
#[snafu(display("faild to configure service"))]
304+
ServiceConfiguration { source: crate::service::Error },
302305
}
303306

304307
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -430,16 +433,29 @@ pub async fn reconcile_superset(
430433
&rbac_sa.name_any(),
431434
&config,
432435
)?;
433-
for rg_service in
434-
build_node_rolegroup_services(superset, &resolved_product_image, &rolegroup)?
435-
{
436-
cluster_resources
437-
.add(client, rg_service)
438-
.await
439-
.with_context(|_| ApplyRoleGroupServiceSnafu {
440-
rolegroup: rolegroup.clone(),
441-
})?;
442-
}
436+
437+
let rg_metrics_service =
438+
build_node_rolegroup_metrics_service(superset, &resolved_product_image, &rolegroup)
439+
.context(ServiceConfigurationSnafu)?;
440+
441+
let rg_headless_service =
442+
build_node_rolegroup_headless_service(superset, &resolved_product_image, &rolegroup)
443+
.context(ServiceConfigurationSnafu)?;
444+
445+
cluster_resources
446+
.add(client, rg_metrics_service)
447+
.await
448+
.with_context(|_| ApplyRoleGroupServiceSnafu {
449+
rolegroup: rolegroup.clone(),
450+
})?;
451+
452+
cluster_resources
453+
.add(client, rg_headless_service)
454+
.await
455+
.with_context(|_| ApplyRoleGroupServiceSnafu {
456+
rolegroup: rolegroup.clone(),
457+
})?;
458+
443459
cluster_resources
444460
.add(client, rg_configmap)
445461
.await
@@ -619,96 +635,10 @@ fn build_rolegroup_config_map(
619635
})
620636
}
621637

622-
/// The rolegroup [`Service`] is a headless service that allows direct access to the instances of a certain rolegroup
623-
///
624-
/// This is mostly useful for internal communication between peers, or for clients that perform client-side load balancing.
625-
fn build_node_rolegroup_services(
626-
superset: &SupersetCluster,
627-
resolved_product_image: &ResolvedProductImage,
628-
rolegroup: &RoleGroupRef<SupersetCluster>,
629-
) -> Result<Vec<Service>> {
630-
let service = vec![
631-
Service {
632-
metadata: ObjectMetaBuilder::new()
633-
.name_and_namespace(superset)
634-
.name(superset.rolegroup_headless_metrics_service_name(rolegroup))
635-
.ownerreference_from_resource(superset, None, Some(true))
636-
.context(ObjectMissingMetadataForOwnerRefSnafu)?
637-
.with_recommended_labels(build_recommended_labels(
638-
superset,
639-
SUPERSET_CONTROLLER_NAME,
640-
&resolved_product_image.app_version_label,
641-
&rolegroup.role,
642-
&rolegroup.role_group,
643-
))
644-
.context(MetadataBuildSnafu)?
645-
.with_label(
646-
Label::try_from(("prometheus.io/scrape", "true")).context(LabelBuildSnafu)?,
647-
)
648-
.build(),
649-
spec: Some(ServiceSpec {
650-
// Internal communication does not need to be exposed
651-
type_: Some("ClusterIP".to_owned()),
652-
cluster_ip: Some("None".to_owned()),
653-
ports: Some(superset.metrics_ports()),
654-
selector: Some(
655-
Labels::role_group_selector(
656-
superset,
657-
APP_NAME,
658-
&rolegroup.role,
659-
&rolegroup.role_group,
660-
)
661-
.context(LabelBuildSnafu)?
662-
.into(),
663-
),
664-
publish_not_ready_addresses: Some(true),
665-
..ServiceSpec::default()
666-
}),
667-
status: None,
668-
},
669-
Service {
670-
metadata: ObjectMetaBuilder::new()
671-
.name_and_namespace(superset)
672-
.name(superset.rolegroup_headless_service_name(rolegroup))
673-
.ownerreference_from_resource(superset, None, Some(true))
674-
.context(ObjectMissingMetadataForOwnerRefSnafu)?
675-
.with_recommended_labels(build_recommended_labels(
676-
superset,
677-
SUPERSET_CONTROLLER_NAME,
678-
&resolved_product_image.app_version_label,
679-
&rolegroup.role,
680-
&rolegroup.role_group,
681-
))
682-
.context(MetadataBuildSnafu)?
683-
.build(),
684-
spec: Some(ServiceSpec {
685-
// Internal communication does not need to be exposed
686-
type_: Some("ClusterIP".to_owned()),
687-
cluster_ip: Some("None".to_owned()),
688-
ports: Some(superset.service_ports()),
689-
selector: Some(
690-
Labels::role_group_selector(
691-
superset,
692-
APP_NAME,
693-
&rolegroup.role,
694-
&rolegroup.role_group,
695-
)
696-
.context(LabelBuildSnafu)?
697-
.into(),
698-
),
699-
publish_not_ready_addresses: Some(true),
700-
..ServiceSpec::default()
701-
}),
702-
status: None,
703-
},
704-
];
705-
706-
Ok(service)
707-
}
708-
709638
/// The rolegroup [`StatefulSet`] runs the rolegroup, as configured by the administrator.
710639
///
711-
/// The [`Pod`](`stackable_operator::k8s_openapi::api::core::v1::Pod`)s are accessible through the corresponding [`Service`] (from [`build_node_rolegroup_services`]).
640+
/// The [`Pod`](`stackable_operator::k8s_openapi::api::core::v1::Pod`)s are accessible through the corresponding
641+
/// [`Service`](`stackable_operator::k8s_openapi::api::core::v1::Service`) (from [`build_node_rolegroup_headless_service`] and [`build_node_rolegroup_metrics_service`]).
712642
#[allow(clippy::too_many_arguments)]
713643
fn build_server_rolegroup_statefulset(
714644
superset: &SupersetCluster,

0 commit comments

Comments
 (0)