diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index eb4b7e0f..22a4f724 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Support specifying externalTrafficPolicy in Services created by listener-operator ([#773]). +- BREAKING: Rename `commons::listener::ServiceType` to `commons::listener::KubernetesServiceType` ([#773]). + +[#773]: https://github.com/stackabletech/operator-rs/pull/773 + ## [0.67.1] - 2024-05-08 ### Added diff --git a/crates/stackable-operator/src/cluster_resources.rs b/crates/stackable-operator/src/cluster_resources.rs index e39ea1c3..65a804d4 100644 --- a/crates/stackable-operator/src/cluster_resources.rs +++ b/crates/stackable-operator/src/cluster_resources.rs @@ -4,6 +4,7 @@ use crate::{ client::{Client, GetApi}, commons::{ cluster_operation::ClusterOperation, + listener::Listener, resources::{ ComputeResource, ResourceRequirementsExt, ResourceRequirementsType, LIMIT_REQUEST_RATIO_CPU, LIMIT_REQUEST_RATIO_MEMORY, @@ -203,6 +204,7 @@ impl ClusterResource for Service {} impl ClusterResource for ServiceAccount {} impl ClusterResource for RoleBinding {} impl ClusterResource for PodDisruptionBudget {} +impl ClusterResource for Listener {} impl ClusterResource for Job { fn pod_spec(&self) -> Option<&PodSpec> { @@ -612,6 +614,7 @@ impl ClusterResources { self.delete_orphaned_resources_of_kind::(client), self.delete_orphaned_resources_of_kind::(client), self.delete_orphaned_resources_of_kind::(client), + self.delete_orphaned_resources_of_kind::(client), )?; Ok(()) diff --git a/crates/stackable-operator/src/commons/listener.rs b/crates/stackable-operator/src/commons/listener.rs index fca90059..84a0b4ad 100644 --- a/crates/stackable-operator/src/commons/listener.rs +++ b/crates/stackable-operator/src/commons/listener.rs @@ -27,6 +27,7 @@ use std::collections::BTreeMap; +use derivative::Derivative; use kube::CustomResource; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -50,7 +51,7 @@ use crate::builder::pod::volume::ListenerOperatorVolumeSourceBuilder; )] #[serde(rename_all = "camelCase")] pub struct ListenerClassSpec { - pub service_type: ServiceType, + pub service_type: KubernetesServiceType, /// Annotations that should be added to the Service object. #[serde(default)] @@ -58,16 +59,37 @@ pub struct ListenerClassSpec { } /// The method used to access the services. -#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema, PartialEq, Eq)] -pub enum ServiceType { +// +// Please note that this represents a Kubernetes type, so the name of the enum variant needs to exactly match the +// Kubernetes service type. +#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema, PartialEq, Eq, strum::Display)] +pub enum KubernetesServiceType { /// Reserve a port on each node. NodePort, + /// Provision a dedicated load balancer. LoadBalancer, + /// Assigns an IP address from a pool of IP addresses that your cluster has reserved for that purpose. ClusterIP, } +/// Service Internal Traffic Policy enables internal traffic restrictions to only route internal traffic to endpoints +/// within the node the traffic originated from. The "internal" traffic here refers to traffic originated from Pods in +/// the current cluster. This can help to reduce costs and improve performance. +/// See [Kubernetes docs](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/). +// +// Please note that this represents a Kubernetes type, so the name of the enum variant needs to exactly match the +// Kubernetes traffic policy. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq, strum::Display)] +pub enum KubernetesTrafficPolicy { + /// Obscures the client source IP and may cause a second hop to another node, but allows Kubernetes to spread the load between all nodes. + Cluster, + + /// Preserves the client source IP and avoid a second hop for LoadBalancer and NodePort type Services, but makes clients responsible for spreading the load. + Local, +} + /// Exposes a set of pods to the outside world. /// /// Essentially a Stackable extension of a Kubernetes Service. Compared to a Service, a Listener changes three things: @@ -78,8 +100,9 @@ pub enum ServiceType { /// /// Learn more in the [Listener documentation](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listener). #[derive( - CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq, + CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq, Derivative, )] +#[derivative(Default)] #[kube( group = "listeners.stackable.tech", version = "v1alpha1", @@ -100,14 +123,27 @@ pub struct ListenerSpec { pub ports: Option>, /// Whether incoming traffic should also be directed to Pods that are not `Ready`. - #[schemars(default = "Self::default_publish_not_ready_addresses")] + #[serde(default = "ListenerSpec::default_publish_not_ready_addresses")] pub publish_not_ready_addresses: Option, + + /// `externalTrafficPolicy` that should be set on the [`Service`] object. + /// + /// The default is `Local` (in contrast to `Cluster`), as we aim to direct traffic to a node running the workload + /// and we should keep testing that as the primary configuration. Cluster is a fallback option for providers that + /// break Local mode (IONOS so far). + #[derivative(Default(value = "ListenerSpec::default_service_external_traffic_policy()"))] + #[serde(default = "ListenerSpec::default_service_external_traffic_policy")] + pub service_external_traffic_policy: KubernetesTrafficPolicy, } impl ListenerSpec { const fn default_publish_not_ready_addresses() -> Option { Some(true) } + + const fn default_service_external_traffic_policy() -> KubernetesTrafficPolicy { + KubernetesTrafficPolicy::Local + } } #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]