Skip to content

Commit 380ff9b

Browse files
committed
Make preferred NodePort address type configurable
1 parent b01da45 commit 380ff9b

File tree

8 files changed

+145
-32
lines changed

8 files changed

+145
-32
lines changed

Cargo.lock

Lines changed: 17 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.nix

Lines changed: 56 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ prost = "0.13"
2121
prost-types = "0.13"
2222
serde = "1.0"
2323
snafu = "0.8"
24-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.76.0" }
24+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.78.0" }
2525
strum = { version = "0.26", features = ["derive"] }
2626
socket2 = { version = "0.5", features = ["all"] }
2727
tokio = { version = "1.40", features = ["full"] }
@@ -31,5 +31,7 @@ tonic-build = "0.12"
3131
tonic-reflection = "0.12"
3232
tracing = "0.1.40"
3333

34-
# [patch."https://github.com/stackabletech/operator-rs.git"]
34+
[patch."https://github.com/stackabletech/operator-rs.git"]
35+
# stackable-operator = { path = "../operator-rs/crates/stackable-operator" }
3536
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
37+
stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feature/listenerclass-preferred-address-type" }

crate-hashes.json

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ spec:
2424
spec:
2525
description: Defines a policy for how [Listeners](https://docs.stackable.tech/home/nightly/listener-operator/listener) should be exposed. Read the [ListenerClass documentation](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass) for more information.
2626
properties:
27+
preferredAddressType:
28+
default: Hostname
29+
description: |-
30+
Whether addresses should prefer using the IP address (`IP`) or the hostname (`Hostname`).
31+
32+
The other type will be used if the preferred type is not available.
33+
enum:
34+
- Hostname
35+
- IP
36+
type: string
2737
serviceAnnotations:
2838
additionalProperties:
2939
type: string

rust/operator-binary/src/csi_server/node.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use snafu::{OptionExt, ResultExt, Snafu};
44
use stackable_operator::{
55
builder::meta::OwnerReferenceBuilder,
66
commons::listener::{
7-
Listener, ListenerIngress, ListenerPort, ListenerSpec, PodListener, PodListenerScope,
8-
PodListeners, PodListenersSpec,
7+
Listener, ListenerClass, ListenerIngress, ListenerPort, ListenerSpec, PodListener,
8+
PodListenerScope, PodListeners, PodListenersSpec,
99
},
1010
k8s_openapi::api::core::v1::{Node, PersistentVolume, PersistentVolumeClaim, Pod, Volume},
1111
kube::{
@@ -72,6 +72,9 @@ enum PublishVolumeError {
7272
listener: ObjectRef<Listener>,
7373
},
7474

75+
#[snafu(display("{listener} has no associated ListenerClass"))]
76+
ListenerHasNoClass { listener: ObjectRef<Listener> },
77+
7578
#[snafu(display("{pod} has not been scheduled to a node yet"))]
7679
PodHasNoNode { pod: ObjectRef<Pod> },
7780

@@ -131,6 +134,7 @@ impl From<PublishVolumeError> for Status {
131134
PublishVolumeError::PodHasNoNode { .. } => Status::unavailable(full_msg),
132135
PublishVolumeError::ListenerPvReference { .. } => Status::failed_precondition(full_msg),
133136
PublishVolumeError::ListenerPodSelector { .. } => Status::failed_precondition(full_msg),
137+
PublishVolumeError::ListenerHasNoClass { .. } => Status::failed_precondition(full_msg),
134138
PublishVolumeError::BuildListenerOwnerRef { .. } => Status::unavailable(full_msg),
135139
PublishVolumeError::ApplyListener { .. } => Status::unavailable(full_msg),
136140
PublishVolumeError::AddListenerLabelToPv { .. } => Status::unavailable(full_msg),
@@ -442,10 +446,25 @@ async fn local_listener_addresses_for_pod(
442446
.get::<Node>(node_name, &())
443447
.await
444448
.with_context(|_| GetObjectSnafu {
445-
obj: { ObjectRef::<Node>::new(node_name).erase() },
449+
obj: ObjectRef::<Node>::new(node_name).erase(),
450+
})?;
451+
let listener_class_name =
452+
listener
453+
.spec
454+
.class_name
455+
.as_deref()
456+
.with_context(|| ListenerHasNoClassSnafu {
457+
listener: ObjectRef::from_obj(listener),
458+
})?;
459+
let listener_class = client
460+
.get::<ListenerClass>(listener_class_name, &())
461+
.await
462+
.with_context(|_| GetObjectSnafu {
463+
obj: ObjectRef::<ListenerClass>::new(listener_class_name).erase(),
446464
})?;
447465

448466
Ok(node_primary_address(&node)
467+
.pick(listener_class.spec.preferred_address_type)
449468
.map(|(address, address_type)| ListenerIngress {
450469
// nodes: Some(vec![node_name.to_string()]),
451470
address: address.to_string(),

rust/operator-binary/src/listener_controller.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,9 @@ pub async fn reconcile(listener: Arc<Listener>, ctx: Arc<Ctx>) -> Result<control
286286
.await?;
287287
addresses = nodes
288288
.iter()
289-
.flat_map(node_primary_address)
289+
.flat_map(|node| {
290+
node_primary_address(node).pick(listener_class.spec.preferred_address_type)
291+
})
290292
.collect::<Vec<_>>();
291293
ports = svc
292294
.spec

rust/operator-binary/src/utils.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,26 +101,42 @@ pub fn error_full_message(err: &dyn std::error::Error) -> String {
101101
full_msg
102102
}
103103

104+
#[derive(Debug, Clone, Copy)]
105+
pub struct AddressCandidates<'a> {
106+
pub ip: Option<&'a str>,
107+
pub hostname: Option<&'a str>,
108+
}
109+
110+
impl<'a> AddressCandidates<'a> {
111+
pub fn pick(&self, preferred_address_type: AddressType) -> Option<(&'a str, AddressType)> {
112+
let ip = self.ip.zip(Some(AddressType::Ip));
113+
let hostname = self.hostname.zip(Some(AddressType::Hostname));
114+
match preferred_address_type {
115+
AddressType::Ip => ip.or(hostname),
116+
AddressType::Hostname => hostname.or(ip),
117+
}
118+
}
119+
}
120+
104121
/// Try to guess the primary address of a Node, which it is expected that external clients should be able to reach it on
105-
pub fn node_primary_address(node: &Node) -> Option<(&str, AddressType)> {
122+
pub fn node_primary_address(node: &Node) -> AddressCandidates {
106123
let addrs = node
107124
.status
108125
.as_ref()
109126
.and_then(|s| s.addresses.as_deref())
110127
.unwrap_or_default();
111-
// IP addresses are currently preferred over hostnames since nodes don't always have resolvable hostnames
112-
addrs
113-
.iter()
114-
.find(|addr| addr.type_ == "ExternalIP")
115-
.or_else(|| addrs.iter().find(|addr| addr.type_ == "InternalIP"))
116-
.zip(Some(AddressType::Ip))
117-
.or_else(|| {
118-
addrs
119-
.iter()
120-
.find(|addr| addr.type_ == "Hostname")
121-
.zip(Some(AddressType::Hostname))
122-
})
123-
.map(|(addr, ty)| (addr.address.as_str(), ty))
128+
129+
AddressCandidates {
130+
ip: addrs
131+
.iter()
132+
.find(|addr| addr.type_ == "ExternalIP")
133+
.or_else(|| addrs.iter().find(|addr| addr.type_ == "InternalIP"))
134+
.map(|addr| addr.address.as_str()),
135+
hostname: addrs
136+
.iter()
137+
.find(|addr| addr.type_ == "Hostname")
138+
.map(|addr| addr.address.as_str()),
139+
}
124140
}
125141

126142
#[cfg(test)]

0 commit comments

Comments
 (0)