Skip to content

Commit 2c253e8

Browse files
committed
added listener dirs and service
1 parent eefb8fa commit 2c253e8

12 files changed

Lines changed: 129 additions & 530 deletions

File tree

deploy/helm/opensearch-operator/templates/roles.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ rules:
7070
- customresourcedefinitions
7171
verbs:
7272
- get
73+
- apiGroups:
74+
- listeners.stackable.tech
75+
resources:
76+
- listeners
77+
verbs:
78+
- get
79+
- list
80+
- watch
81+
- patch
82+
- create
83+
- delete
7384
- apiGroups:
7485
- events.k8s.io
7586
resources:

rust/operator-binary/src/controller.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use snafu::{ResultExt, Snafu};
66
use stackable_operator::{
77
cluster_resources::ClusterResourceApplyStrategy,
88
commons::{affinity::StackableAffinity, product_image_selection::ProductImage},
9+
crd::listener::v1alpha1::Listener,
910
k8s_openapi::api::{
1011
apps::v1::StatefulSet,
1112
core::v1::{ConfigMap, Service, ServiceAccount},
@@ -111,6 +112,7 @@ pub struct ValidatedOpenSearchConfig {
111112
pub node_roles: NodeRoles,
112113
pub resources: stackable_operator::commons::resources::Resources<v1alpha1::StorageConfig>,
113114
pub termination_grace_period_seconds: i64,
115+
pub listener_class: String,
114116
}
115117

116118
// validated and converted to validated and safe types
@@ -275,6 +277,7 @@ struct Applied;
275277
struct KubernetesResources<T> {
276278
stateful_sets: Vec<StatefulSet>,
277279
services: Vec<Service>,
280+
listeners: Vec<Listener>,
278281
config_maps: Vec<ConfigMap>,
279282
service_accounts: Vec<ServiceAccount>,
280283
role_bindings: Vec<RoleBinding>,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ impl<'a> Applier<'a> {
6262

6363
let services = self.add_resources(resources.services).await?;
6464

65+
let listeners = self.add_resources(resources.listeners).await?;
66+
6567
let config_maps = self.add_resources(resources.config_maps).await?;
6668

6769
let service_accounts = self.add_resources(resources.service_accounts).await?;
@@ -78,6 +80,7 @@ impl<'a> Applier<'a> {
7880
Ok(KubernetesResources {
7981
stateful_sets,
8082
services,
83+
listeners,
8184
config_maps,
8285
service_accounts,
8386
role_bindings,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou
1212
let mut config_maps = vec![];
1313
let mut stateful_sets = vec![];
1414
let mut services = vec![];
15+
let mut listeners = vec![];
1516

1617
let role_builder = RoleBuilder::new(cluster.clone(), names);
1718

1819
for role_group_builder in role_builder.role_group_builders() {
1920
config_maps.push(role_group_builder.build_config_map());
2021
stateful_sets.push(role_group_builder.build_stateful_set());
2122
services.push(role_group_builder.build_headless_service());
23+
listeners.push(role_group_builder.build_listener());
2224
}
2325

2426
let cluster_manager_service = role_builder.build_cluster_manager_service();
@@ -33,6 +35,7 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou
3335
KubernetesResources {
3436
stateful_sets,
3537
services,
38+
listeners,
3639
config_maps,
3740
service_accounts,
3841
role_bindings,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ mod tests {
286286
node_roles: NodeRoles::default(),
287287
resources: Resources::default(),
288288
termination_grace_period_seconds: 30,
289+
listener_class: "cluster-internal".to_string(),
289290
},
290291
config_overrides: HashMap::default(),
291292
env_overrides: [("TEST".to_owned(), "value".to_owned())].into(),

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

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
use stackable_operator::{
2-
builder::{meta::ObjectMetaBuilder, pod::container::ContainerBuilder},
2+
builder::{
3+
meta::ObjectMetaBuilder,
4+
pod::{
5+
container::ContainerBuilder,
6+
volume::{ListenerOperatorVolumeSourceBuilder, ListenerReference},
7+
},
8+
},
9+
crd::listener::{self},
310
k8s_openapi::{
411
DeepMerge,
512
api::{
613
apps::v1::{StatefulSet, StatefulSetSpec},
714
core::v1::{
815
Affinity, ConfigMap, ConfigMapVolumeSource, Container, ContainerPort,
9-
PodSecurityContext, PodSpec, PodTemplateSpec, Probe, Service, ServicePort,
10-
ServiceSpec, TCPSocketAction, Volume, VolumeMount,
16+
PersistentVolumeClaim, PodSecurityContext, PodSpec, PodTemplateSpec, Probe,
17+
Service, ServicePort, ServiceSpec, TCPSocketAction, Volume, VolumeMount,
1118
},
1219
},
1320
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
@@ -36,8 +43,11 @@ pub const TRANSPORT_PORT: u16 = 9300;
3643
const CONFIG_VOLUME_NAME: &str = "config";
3744
const DATA_VOLUME_NAME: &str = "data";
3845

46+
const LISTENER_VOLUME_NAME: &str = "listener";
47+
const LISTENER_VOLUME_DIR: &str = "/stackable/listener";
48+
3949
// Path in opensearchproject/opensearch:3.0.0
40-
const OPENSEARCH_BASE_PATH: &str = "/usr/share/opensearch";
50+
const OPENSEARCH_BASE_PATH: &str = "/stackable/opensearch/";
4151

4252
pub struct RoleGroupBuilder<'a> {
4353
service_account_name: String,
@@ -107,6 +117,27 @@ impl<'a> RoleGroupBuilder<'a> {
107117
.data
108118
.build_pvc(DATA_VOLUME_NAME, Some(vec!["ReadWriteOnce"]));
109119

120+
let listener_group_name = self.resource_names.listener_service_name();
121+
122+
// Listener endpoints for the all rolegroups will use persistent
123+
// volumes so that load balancers can hard-code the target
124+
// addresses. This will be the case even when no class is set (and
125+
// the value defaults to cluster-internal) as the address should
126+
// still be consistent.
127+
let listener_volume_claim_template = ListenerOperatorVolumeSourceBuilder::new(
128+
&ListenerReference::ListenerName(listener_group_name),
129+
// TODO should be unversioned
130+
&self.recommended_labels(),
131+
)
132+
.expect("should be a listener group name")
133+
.build_pvc(LISTENER_VOLUME_NAME.to_string())
134+
.expect("should be a valid annotation");
135+
136+
let pvcs: Option<Vec<PersistentVolumeClaim>> = Some(vec![
137+
data_volume_claim_template,
138+
listener_volume_claim_template,
139+
]);
140+
110141
let spec = StatefulSetSpec {
111142
// Order does not matter for OpenSearch
112143
pod_management_policy: Some("Parallel".to_string()),
@@ -117,7 +148,7 @@ impl<'a> RoleGroupBuilder<'a> {
117148
},
118149
service_name: Some(self.resource_names.headless_service_name()),
119150
template,
120-
volume_claim_templates: Some(vec![data_volume_claim_template]),
151+
volume_claim_templates: pvcs,
121152
..StatefulSetSpec::default()
122153
};
123154

@@ -271,6 +302,11 @@ impl<'a> RoleGroupBuilder<'a> {
271302
name: DATA_VOLUME_NAME.to_owned(),
272303
..VolumeMount::default()
273304
},
305+
VolumeMount {
306+
mount_path: LISTENER_VOLUME_DIR.to_owned(),
307+
name: LISTENER_VOLUME_NAME.to_owned(),
308+
..VolumeMount::default()
309+
},
274310
])
275311
.expect("The mount paths are statically defined and there should be no duplicates.")
276312
.add_container_ports(vec![
@@ -337,6 +373,33 @@ impl<'a> RoleGroupBuilder<'a> {
337373
}
338374
}
339375

376+
pub fn build_listener(&self) -> listener::v1alpha1::Listener {
377+
let metadata =
378+
self.common_metadata(self.resource_names.listener_service_name(), Labels::new());
379+
380+
let listener_class = self.role_group_config.config.listener_class.to_owned();
381+
382+
listener::v1alpha1::Listener {
383+
metadata,
384+
spec: listener::v1alpha1::ListenerSpec {
385+
class_name: Some(listener_class),
386+
ports: Some(self.listener_ports()),
387+
..listener::v1alpha1::ListenerSpec::default()
388+
},
389+
status: None,
390+
}
391+
}
392+
393+
/// We only use the http port here and intentionally omit
394+
/// the metrics one.
395+
fn listener_ports(&self) -> Vec<listener::v1alpha1::ListenerPort> {
396+
vec![listener::v1alpha1::ListenerPort {
397+
name: HTTP_PORT_NAME.to_string(),
398+
port: HTTP_PORT.into(),
399+
protocol: Some("TCP".to_string()),
400+
}]
401+
}
402+
340403
fn common_metadata(
341404
&self,
342405
resource_name: impl Into<String>,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ fn validate_role_group_config(
122122
node_roles: merged_role_group.config.config.node_roles,
123123
resources: merged_role_group.config.config.resources,
124124
termination_grace_period_seconds,
125+
listener_class: merged_role_group.config.config.listener_class,
125126
};
126127

127128
Ok(RoleGroupConfig {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use crate::framework::{
3030
role_utils::GenericProductSpecificCommonConfig,
3131
};
3232

33+
const DEFAULT_LISTENER_CLASS: &str = "cluster-internal";
34+
3335
#[versioned(version(name = "v1alpha1"))]
3436
pub mod versioned {
3537

@@ -130,6 +132,10 @@ pub mod versioned {
130132

131133
#[fragment_attrs(serde(default))]
132134
pub resources: Resources<StorageConfig>,
135+
136+
/// This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the webserver.
137+
#[serde(default = "default_listener_class")]
138+
pub listener_class: String,
133139
}
134140

135141
#[derive(Clone, Debug, Default, JsonSchema, PartialEq, Fragment)]
@@ -162,6 +168,10 @@ pub mod versioned {
162168
}
163169
}
164170

171+
fn default_listener_class() -> String {
172+
DEFAULT_LISTENER_CLASS.to_string()
173+
}
174+
165175
impl HasStatusCondition for v1alpha1::OpenSearchCluster {
166176
fn conditions(&self) -> Vec<ClusterCondition> {
167177
match &self.status {
@@ -232,6 +242,7 @@ impl v1alpha1::OpenSearchConfig {
232242
},
233243
},
234244
},
245+
listener_class: Some("cluster-internal".to_string()),
235246
}
236247
}
237248
}

rust/operator-binary/src/framework/role_group_utils.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ impl ResourceNames {
6565

6666
format!("{}{SUFFIX}", self.qualified_role_group_name())
6767
}
68+
69+
pub fn listener_service_name(&self) -> String {
70+
// Compile-time check
71+
const _: () = assert!(
72+
ResourceNames::MAX_QUALIFIED_ROLE_GROUP_NAME_LENGTH <= MAX_OBJECT_NAME_LENGTH,
73+
"The listener name `<cluster_name>-<role_name>-<role_group_name>` must not exceed 253 characters."
74+
);
75+
76+
self.qualified_role_group_name()
77+
}
6878
}
6979

7080
#[cfg(test)]

0 commit comments

Comments
 (0)