Skip to content

Commit 5f7fd70

Browse files
sbernauerTechassi
andauthored
Support listing endpoints of Listeners in "stacklet list" command (#213)
* Support listing endpoints of Listeners in "stacklet list" command * changelog * improve docs * doc comment * Move endpoints declaration down * Update rust/stackable-cockpit/src/platform/service.rs Co-authored-by: Techassi <git@techassi.dev> * Move stuff into display_name_for_listener_name function --------- Co-authored-by: Techassi <git@techassi.dev>
1 parent 5837276 commit 5f7fd70

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

rust/stackable-cockpit/src/platform/service.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,46 @@ pub async fn get_endpoints(
4545
object_name: &str,
4646
object_namespace: &str,
4747
) -> Result<IndexMap<String, String>, Error> {
48-
let service_list_params =
48+
let list_params =
4949
ListParams::from_product(product_name, Some(object_name), k8s::ProductLabel::Name);
5050

51-
let services = kube_client
52-
.list_services(Some(object_namespace), &service_list_params)
51+
let listeners = kube_client
52+
.list_listeners(Some(object_namespace), &list_params)
5353
.await
5454
.context(KubeClientFetchSnafu)?;
5555

5656
let mut endpoints = IndexMap::new();
57+
for listener in &listeners {
58+
let Some(display_name) = display_name_for_listener_name(&listener.name_any(), object_name)
59+
else {
60+
continue;
61+
};
62+
let Some(listener_status) = &listener.status else {
63+
continue;
64+
};
65+
66+
for address in listener_status.ingress_addresses.iter().flatten() {
67+
for port in &address.ports {
68+
let text = format!("{display_name}-{port_name}", port_name = port.0);
69+
let endpoint_url = endpoint_url(&address.address, *port.1, port.0);
70+
endpoints.insert(text, endpoint_url);
71+
}
72+
}
73+
}
74+
75+
// Ideally we use listener-operator everywhere, afterwards we can remove the whole k8s Services handling below.
76+
// Currently the Services created by listener-op are missing the recommended labels, so this early exit in case we
77+
// find Listeners is currently not required. However, once we add the recommended labels to the k8s Services, we
78+
// would have duplicated entries (one from the Listener and one from the Service). Because of this we don't look at
79+
// the Services in case we found Listeners!
80+
if !listeners.items.is_empty() {
81+
return Ok(endpoints);
82+
}
83+
84+
let services = kube_client
85+
.list_services(Some(object_namespace), &list_params)
86+
.await
87+
.context(KubeClientFetchSnafu)?;
5788

5889
for service in services {
5990
match get_endpoint_urls(kube_client, &service, object_name).await {
@@ -268,7 +299,7 @@ async fn get_node_name_ip_mapping(
268299
}
269300

270301
fn endpoint_url(endpoint_host: &str, endpoint_port: i32, port_name: &str) -> String {
271-
// TODO: Consolidate web-ui port names in operators based on decision in arch meeting from 2022/08/10
302+
// TODO: Consolidate web-ui port names in operators based on decision in arch meeting from 2022-08-10
272303
// For Superset: https://github.com/stackabletech/superset-operator/issues/248
273304
// For Airflow: https://github.com/stackabletech/airflow-operator/issues/146
274305
// As we still support older operator versions we need to also include the "old" way of naming
@@ -285,3 +316,38 @@ fn endpoint_url(endpoint_host: &str, endpoint_port: i32, port_name: &str) -> Str
285316
format!("{endpoint_host}:{endpoint_port}")
286317
}
287318
}
319+
320+
/// Listener names usually have the pattern `listener-simple-hdfs-namenode-default-0` or
321+
/// `simple-hdfs-datanode-default-0-listener`, so we can strip everything before the first occurrence of
322+
/// the stacklet name (`simple-hdfs` in this case). After that it actually get's pretty hard.
323+
/// This truncation is *not* ideal, however we only have implemented listener-operator for HDFS so far,
324+
/// so better to have support for that than nothing :)
325+
fn display_name_for_listener_name(listener_name: &str, object_name: &str) -> Option<String> {
326+
let Some((_, display_name)) = listener_name.split_once(object_name) else {
327+
return None;
328+
};
329+
Some(display_name.trim_start_matches('-').to_owned())
330+
}
331+
332+
#[cfg(test)]
333+
mod tests {
334+
use super::*;
335+
use rstest::rstest;
336+
337+
#[rstest]
338+
// These are all the listener names implemented so far (only HDFS is using listener-operator). In the future more
339+
// test-case should be added.
340+
#[case("listener-simple-hdfs-namenode-default-0", "simple-hdfs", Some("namenode-default-0".to_string()))]
341+
#[case("listener-simple-hdfs-namenode-default-1", "simple-hdfs", Some("namenode-default-1".to_string()))]
342+
// FIXME: Come up with a more clever strategy to remove the `-listener` suffix. I would prefer to wait until we
343+
// actually have more products using listener-op to not accidentally strip to much.
344+
#[case("simple-hdfs-datanode-default-0-listener", "simple-hdfs", Some("datanode-default-0-listener".to_string()))]
345+
fn test_display_name_for_listener_name(
346+
#[case] listener_name: &str,
347+
#[case] object_name: &str,
348+
#[case] expected: Option<String>,
349+
) {
350+
let output = display_name_for_listener_name(listener_name, object_name);
351+
assert_eq!(output, expected);
352+
}
353+
}

rust/stackable-cockpit/src/platform/stacklet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async fn list_stackable_stacklets(
126126
Some(obj) => obj,
127127
None => {
128128
info!(
129-
"Failed to list services because the gvk {product_gvk:?} can not be resolved"
129+
"Failed to list stacklets because the gvk {product_gvk:?} can not be resolved"
130130
);
131131
continue;
132132
}

rust/stackable-cockpit/src/utils/k8s/client.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use kube::{
1212
};
1313
use serde::Deserialize;
1414
use snafu::{OptionExt, ResultExt, Snafu};
15-
use stackable_operator::kvp::Labels;
15+
use stackable_operator::{commons::listener::Listener, kvp::Labels};
1616

1717
use crate::{
1818
platform::{cluster, credentials::Credentials},
@@ -188,7 +188,7 @@ impl Client {
188188
))
189189
}
190190

191-
/// Lists [`Service`]s by matching labels. The services can be matched by
191+
/// Lists [`Service`]s by matching labels. The Services can be matched by
192192
/// the product labels. [`ListParamsExt`] provides a utility function to
193193
/// create [`ListParams`] based on a product name and optional instance
194194
/// name.
@@ -210,6 +210,28 @@ impl Client {
210210
Ok(services)
211211
}
212212

213+
/// Lists [`Listener`]s by matching labels. The Listeners can be matched by
214+
/// the product labels. [`ListParamsExt`] provides a utility function to
215+
/// create [`ListParams`] based on a product name and optional instance
216+
/// name.
217+
pub async fn list_listeners(
218+
&self,
219+
namespace: Option<&str>,
220+
list_params: &ListParams,
221+
) -> ListResult<Listener> {
222+
let listener_api: Api<Listener> = match namespace {
223+
Some(namespace) => Api::namespaced(self.client.clone(), namespace),
224+
None => Api::all(self.client.clone()),
225+
};
226+
227+
let listeners = listener_api
228+
.list(list_params)
229+
.await
230+
.context(KubeClientFetchSnafu)?;
231+
232+
Ok(listeners)
233+
}
234+
213235
/// Retrieves user credentials consisting of username and password from a
214236
/// secret identified by `secret_name` inside the `secret_namespace`. If
215237
/// either one of the values is missing, [`Ok(None)`] is returned. An error

rust/stackablectl/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Support listing endpoints of Listeners in `stackablectl stacklet list` command.
10+
Currently only HDFS is using listener-op, so we can only test that so far ([#213]).
11+
712
### Changed
813

914
- Operators are now installed in parallel when installing a release ([#202]).
@@ -14,6 +19,7 @@ All notable changes to this project will be documented in this file.
1419

1520
[#181]: https://github.com/stackabletech/stackable-cockpit/pull/181
1621
[#202]: https://github.com/stackabletech/stackable-cockpit/pull/202
22+
[#213]: https://github.com/stackabletech/stackable-cockpit/pull/213
1723

1824
## [23.11.3] - 2024-01-03
1925

0 commit comments

Comments
 (0)