Skip to content

Commit ccf6161

Browse files
feat: Add listener support (#17)
* added listener dirs and service * added integration test; use unversioned labels for STS listener volume * changelog, docs * Update docs/modules/opensearch/pages/usage-guide/listenerclass.adoc Co-authored-by: Siegfried Weber <[email protected]> * Update docs/modules/opensearch/pages/usage-guide/listenerclass.adoc Co-authored-by: Siegfried Weber <[email protected]> * Update docs/modules/opensearch/partials/nav.adoc Co-authored-by: Siegfried Weber <[email protected]> * Update rust/operator-binary/src/controller/build/role_group_builder.rs Co-authored-by: Siegfried Weber <[email protected]> * Update rust/operator-binary/src/controller/build/role_group_builder.rs Co-authored-by: Siegfried Weber <[email protected]> * Update rust/operator-binary/src/crd/mod.rs Co-authored-by: Siegfried Weber <[email protected]> * Update tests/templates/kuttl/external-access/20-assert.yaml Co-authored-by: Siegfried Weber <[email protected]> * Update tests/templates/kuttl/smoke/10-assert.yaml Co-authored-by: Siegfried Weber <[email protected]> * Update rust/operator-binary/src/crd/mod.rs Co-authored-by: Siegfried Weber <[email protected]> * reverted assert, removed default function * missed owner ref * added listener test case * use versioned labels, move listener_pvc to framework module --------- Co-authored-by: Siegfried Weber <[email protected]>
1 parent eefb8fa commit ccf6161

File tree

25 files changed

+604
-59
lines changed

25 files changed

+604
-59
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ All notable changes to this project will be documented in this file.
1616
- Resources (CPU, memory, storage)
1717
- PodDisruptionBudgets
1818
- Replicas
19+
- Add Listener support ([#17]).
1920

2021
[#10]: https://github.com/stackabletech/opensearch-operator/pull/10
22+
[#17]: https://github.com/stackabletech/opensearch-operator/pull/17

deploy/helm/opensearch-operator/crds/crds.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ spec:
133133
description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details.
134134
nullable: true
135135
type: string
136+
listenerClass:
137+
description: This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication.
138+
nullable: true
139+
type: string
136140
nodeRoles:
137141
items:
138142
enum:
@@ -325,6 +329,10 @@ spec:
325329
description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details.
326330
nullable: true
327331
type: string
332+
listenerClass:
333+
description: This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication.
334+
nullable: true
335+
type: string
328336
nodeRoles:
329337
items:
330338
enum:

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:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
= Service exposition with ListenerClasses
2+
:description: Configure OpenSearch service exposure with ListenerClasses: cluster-internal, external-unstable, or external-stable.
3+
4+
The operator deploys a xref:listener-operator:listener.adoc[Listener] for OpenSearch role-groups.
5+
The listener defaults to only being accessible from within the Kubernetes cluster, but this can be changed by setting `.spec.nodes.roleGroups.\{role-group-name}.config.listenerClass`:
6+
7+
[source,yaml]
8+
----
9+
spec:
10+
nodes:
11+
roleGroups:
12+
cluster-manager:
13+
config:
14+
listenerClass: external-stable # <1>
15+
----
16+
<1> Specify a ListenerClass, such as `external-stable`, `external-unstable`, or `cluster-internal` (the default setting is `cluster-internal`) at role-group level.
17+
This can be set for all role-groups individually.

docs/modules/opensearch/partials/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
** xref:opensearch:getting_started/first_steps.adoc[]
44
* xref:opensearch:usage-guide/index.adoc[]
55
** xref:opensearch:usage-guide/node-roles.adoc[]
6+
** xref:opensearch:usage-guide/listenerclass.adoc[]
67
** xref:opensearch:usage-guide/storage-resource-configuration.adoc[]
78
** xref:opensearch:usage-guide/configuration-environment-overrides.adoc[]
89
** xref:opensearch:usage-guide/operations/index.adoc[]

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: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use stackable_operator::{
22
builder::{meta::ObjectMetaBuilder, pod::container::ContainerBuilder},
3+
crd::listener::{self},
34
k8s_openapi::{
45
DeepMerge,
56
api::{
67
apps::v1::{StatefulSet, StatefulSetSpec},
78
core::v1::{
89
Affinity, ConfigMap, ConfigMapVolumeSource, Container, ContainerPort,
9-
PodSecurityContext, PodSpec, PodTemplateSpec, Probe, Service, ServicePort,
10-
ServiceSpec, TCPSocketAction, Volume, VolumeMount,
10+
PersistentVolumeClaim, PodSecurityContext, PodSpec, PodTemplateSpec, Probe,
11+
Service, ServicePort, ServiceSpec, TCPSocketAction, Volume, VolumeMount,
1112
},
1213
},
1314
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
@@ -24,6 +25,7 @@ use crate::{
2425
RoleGroupName,
2526
builder::meta::ownerreference_from_resource,
2627
kvp::label::{recommended_labels, role_group_selector, role_selector},
28+
listener::listener_pvc,
2729
role_group_utils::ResourceNames,
2830
},
2931
};
@@ -36,8 +38,11 @@ pub const TRANSPORT_PORT: u16 = 9300;
3638
const CONFIG_VOLUME_NAME: &str = "config";
3739
const DATA_VOLUME_NAME: &str = "data";
3840

41+
const LISTENER_VOLUME_NAME: &str = "listener";
42+
const LISTENER_VOLUME_DIR: &str = "/stackable/listener";
43+
3944
// Path in opensearchproject/opensearch:3.0.0
40-
const OPENSEARCH_BASE_PATH: &str = "/usr/share/opensearch";
45+
const OPENSEARCH_BASE_PATH: &str = "/stackable/opensearch";
4146

4247
pub struct RoleGroupBuilder<'a> {
4348
service_account_name: String,
@@ -107,6 +112,24 @@ impl<'a> RoleGroupBuilder<'a> {
107112
.data
108113
.build_pvc(DATA_VOLUME_NAME, Some(vec!["ReadWriteOnce"]));
109114

115+
let listener_group_name = self.resource_names.listener_service_name();
116+
117+
// Listener endpoints for the all rolegroups will use persistent
118+
// volumes so that load balancers can hard-code the target
119+
// addresses. This will be the case even when no class is set (and
120+
// the value defaults to cluster-internal) as the address should
121+
// still be consistent.
122+
let listener_volume_claim_template = listener_pvc(
123+
listener_group_name,
124+
&self.recommended_labels(),
125+
LISTENER_VOLUME_NAME.to_string(),
126+
);
127+
128+
let pvcs: Option<Vec<PersistentVolumeClaim>> = Some(vec![
129+
data_volume_claim_template,
130+
listener_volume_claim_template,
131+
]);
132+
110133
let spec = StatefulSetSpec {
111134
// Order does not matter for OpenSearch
112135
pod_management_policy: Some("Parallel".to_string()),
@@ -117,7 +140,7 @@ impl<'a> RoleGroupBuilder<'a> {
117140
},
118141
service_name: Some(self.resource_names.headless_service_name()),
119142
template,
120-
volume_claim_templates: Some(vec![data_volume_claim_template]),
143+
volume_claim_templates: pvcs,
121144
..StatefulSetSpec::default()
122145
};
123146

@@ -271,6 +294,11 @@ impl<'a> RoleGroupBuilder<'a> {
271294
name: DATA_VOLUME_NAME.to_owned(),
272295
..VolumeMount::default()
273296
},
297+
VolumeMount {
298+
mount_path: LISTENER_VOLUME_DIR.to_owned(),
299+
name: LISTENER_VOLUME_NAME.to_owned(),
300+
..VolumeMount::default()
301+
},
274302
])
275303
.expect("The mount paths are statically defined and there should be no duplicates.")
276304
.add_container_ports(vec![
@@ -337,6 +365,33 @@ impl<'a> RoleGroupBuilder<'a> {
337365
}
338366
}
339367

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

0 commit comments

Comments
 (0)