Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- `Listener.status.addresses` can now be configured to prefer either IP addresses or DNS hostnames ([#233]).

### Changed

- Listener.status.addresses for NodePort listeners now includes replicas that are currently unavailable ([#231]).
- `Listener.status.addresses` for NodePort listeners now includes replicas that are currently unavailable ([#231]).
- `Listener.status.addresses` now defaults to DNS hostnames for all service types (previously NodePort and ClusterIP would prefer IP addresses, [#233]).

### Fixed

- Listener.status.addresses is now de-duplicated ([#231]).
- Listener controller now listens for ListenerClass updates ([#231]).

[#231]: https://github.com/stackabletech/listener-operator/pull/231
[#233]: https://github.com/stackabletech/listener-operator/pull/233

## [24.7.0] - 2024-07-24

Expand Down
20 changes: 17 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 56 additions & 7 deletions Cargo.nix

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ prost = "0.13"
prost-types = "0.13"
serde = "1.0"
snafu = "0.8"
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.76.0" }
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.78.0" }
strum = { version = "0.26", features = ["derive"] }
socket2 = { version = "0.5", features = ["all"] }
tokio = { version = "1.40", features = ["full"] }
Expand All @@ -31,5 +31,7 @@ tonic-build = "0.12"
tonic-reflection = "0.12"
tracing = "0.1.40"

# [patch."https://github.com/stackabletech/operator-rs.git"]
[patch."https://github.com/stackabletech/operator-rs.git"]
# stackable-operator = { path = "../operator-rs/crates/stackable-operator" }
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feature/listenerclass-preferred-address-type" }
5 changes: 3 additions & 2 deletions crate-hashes.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions deploy/helm/listener-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ spec:
spec:
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.
properties:
preferredAddressType:
default: Hostname
description: |-
Whether addresses should prefer using the IP address (`IP`) or the hostname (`Hostname`).

The other type will be used if the preferred type is not available.
enum:
- Hostname
- IP
type: string
serviceAnnotations:
additionalProperties:
type: string
Expand Down
19 changes: 19 additions & 0 deletions docs/modules/listener-operator/pages/listenerclass.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ How exactly this is accomplished depends on the cloud provider in question, but
include::example$listenerclass-internal-gke.yaml[]
----

[#servicetype]
== Service types

The service type is defined by `ListenerClass.spec.serviceType`.
Expand Down Expand Up @@ -62,6 +63,24 @@ Compared to xref:#servicetype-nodeport[], this service type allows Pods to be mo
However, it requires https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer[a cloud controller manager that supports load balancers].
Additionally, many cloud providers charge for load-balanced traffic.

[#addresstype]
== Address types

The Stackable Listener Operator supports both IP addresses and DNS hostnames. The preferred address type for a given ListenerClass can be configured using the `ListenerClass.spec.preferredAddressType` field. If no `preferredAddressType` is specified then it defaults to xref:#addresstype-hostname[].

NOTE: If the preferred address type is not supported for a given environment then another type will be used.

[#addresstype-ip]
=== IP

The IP address of a resource. The addresses will be less predictable (especially for xref:#servicetype-clusterip[] services),
but does not require any special client configuration (beyond what the xref:#servicetype[] requires).

[#addresstype-hostname]
=== Hostname

The DNS hostname of a resource. Clients must be able to resolve these addresses in order to connect, which may require special DNS configuration.

== Default ListenerClasses

The Stackable Data Platform assumes the existence of a few predefined ListenerClasses, and will use them by default as appropriate:
Expand Down
2 changes: 1 addition & 1 deletion rust/operator-binary/src/csi_server/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use stackable_operator::{
};
use tonic::{Request, Response, Status};

use crate::utils::error_full_message;
use crate::utils::error::error_full_message;

use super::{tonic_unimplemented, ListenerSelector, ListenerVolumeContext};

Expand Down
29 changes: 24 additions & 5 deletions rust/operator-binary/src/csi_server/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use snafu::{OptionExt, ResultExt, Snafu};
use stackable_operator::{
builder::meta::OwnerReferenceBuilder,
commons::listener::{
Listener, ListenerIngress, ListenerPort, ListenerSpec, PodListener, PodListenerScope,
PodListeners, PodListenersSpec,
Listener, ListenerClass, ListenerIngress, ListenerPort, ListenerSpec, PodListener,
PodListenerScope, PodListeners, PodListenersSpec,
},
k8s_openapi::api::core::v1::{Node, PersistentVolume, PersistentVolumeClaim, Pod, Volume},
kube::{
Expand All @@ -21,7 +21,7 @@ use crate::{
listener_mounted_pod_label, listener_persistent_volume_label, ListenerMountedPodLabelError,
ListenerPersistentVolumeLabelError,
},
utils::{error_full_message, node_primary_address},
utils::{address::node_primary_addresses, error::error_full_message},
};

use super::{tonic_unimplemented, ListenerSelector, ListenerVolumeContext};
Expand Down Expand Up @@ -72,6 +72,9 @@ enum PublishVolumeError {
listener: ObjectRef<Listener>,
},

#[snafu(display("{listener} has no associated ListenerClass"))]
ListenerHasNoClass { listener: ObjectRef<Listener> },

#[snafu(display("{pod} has not been scheduled to a node yet"))]
PodHasNoNode { pod: ObjectRef<Pod> },

Expand Down Expand Up @@ -131,6 +134,7 @@ impl From<PublishVolumeError> for Status {
PublishVolumeError::PodHasNoNode { .. } => Status::unavailable(full_msg),
PublishVolumeError::ListenerPvReference { .. } => Status::failed_precondition(full_msg),
PublishVolumeError::ListenerPodSelector { .. } => Status::failed_precondition(full_msg),
PublishVolumeError::ListenerHasNoClass { .. } => Status::failed_precondition(full_msg),
PublishVolumeError::BuildListenerOwnerRef { .. } => Status::unavailable(full_msg),
PublishVolumeError::ApplyListener { .. } => Status::unavailable(full_msg),
PublishVolumeError::AddListenerLabelToPv { .. } => Status::unavailable(full_msg),
Expand Down Expand Up @@ -442,10 +446,25 @@ async fn local_listener_addresses_for_pod(
.get::<Node>(node_name, &())
.await
.with_context(|_| GetObjectSnafu {
obj: { ObjectRef::<Node>::new(node_name).erase() },
obj: ObjectRef::<Node>::new(node_name).erase(),
})?;
let listener_class_name =
listener
.spec
.class_name
.as_deref()
.with_context(|| ListenerHasNoClassSnafu {
listener: ObjectRef::from_obj(listener),
})?;
let listener_class = client
.get::<ListenerClass>(listener_class_name, &())
.await
.with_context(|_| GetObjectSnafu {
obj: ObjectRef::<ListenerClass>::new(listener_class_name).erase(),
})?;

Ok(node_primary_address(&node)
Ok(node_primary_addresses(&node)
.pick(listener_class.spec.preferred_address_type)
.map(|(address, address_type)| ListenerIngress {
// nodes: Some(vec![node_name.to_string()]),
address: address.to_string(),
Expand Down
Loading
Loading