From e8b93a985fe6ade3470263be8d7c8ef2af19547a Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Mon, 17 Oct 2022 15:20:58 +0200 Subject: [PATCH 1/5] Add ListenerOperatorVolumeSourceBuilder --- CHANGELOG.md | 6 +++ src/builder/pod/volume.rs | 104 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d248fb94..ae3661fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Builder for `EphemeralVolumeSource`s added which are used by the listener-operator ([#496]). + +[#496]: https://github.com/stackabletech/operator-rs/pull/496 + ## [0.26.0] - 2022-10-20 ### Added diff --git a/src/builder/pod/volume.rs b/src/builder/pod/volume.rs index 458798973..83a1c916b 100644 --- a/src/builder/pod/volume.rs +++ b/src/builder/pod/volume.rs @@ -13,6 +13,7 @@ use k8s_openapi::{ use std::collections::BTreeMap; use crate::builder::ObjectMetaBuilder; +use crate::commons::listener; /// A builder to build [`Volume`] objects. /// May only contain one `volume_source` at a time. @@ -339,6 +340,66 @@ enum SecretOperatorVolumeScope { Service { name: String }, } +/// Reference to a listener class or listener name +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ListenerReference { + ListenerClass(String), + ListenerName(String), +} + +impl ListenerReference { + /// Return the key and value for a Kubernetes object annotation + fn to_annotation(&self) -> (String, String) { + match self { + ListenerReference::ListenerClass(value) => ( + "listeners.stackable.tech/listener-class".into(), + value.into(), + ), + ListenerReference::ListenerName(value) => ( + "listeners.stackable.tech/listener-name".into(), + value.into(), + ), + } + } +} + +/// Builder for an [`EphemeralVolumeSource`] containing the listener configuration +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ListenerOperatorVolumeSourceBuilder { + listener_reference: ListenerReference, +} + +impl ListenerOperatorVolumeSourceBuilder { + /// Create a builder for the given listener class or listener name + pub fn new(listener_reference: &ListenerReference) -> Self { + Self { + listener_reference: listener_reference.to_owned(), + } + } + + /// Build an [`EphemeralVolumeSource`] from the builder + pub fn build(&self) -> EphemeralVolumeSource { + EphemeralVolumeSource { + volume_claim_template: Some(PersistentVolumeClaimTemplate { + metadata: Some( + ObjectMetaBuilder::new() + .annotations([self.listener_reference.to_annotation()].into()) + .build(), + ), + spec: PersistentVolumeClaimSpec { + storage_class_name: Some("listeners.stackable.tech".to_string()), + resources: Some(ResourceRequirements { + requests: Some([("storage".to_string(), Quantity("1".to_string()))].into()), + ..ResourceRequirements::default() + }), + access_modes: Some(vec!["ReadWriteMany".to_string()]), + ..PersistentVolumeClaimSpec::default() + }, + }), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -399,4 +460,47 @@ mod tests { assert_eq!(vm.sub_path, Some("sub_path".to_string())); assert_eq!(vm.sub_path_expr, Some("sub_path_expr".to_string())); } + + #[test] + fn test_listener_operator_volume_source_builder() { + let builder = ListenerOperatorVolumeSourceBuilder::new(&ListenerReference::ListenerClass( + "public".into(), + )); + + let volume_source = builder.build(); + + let volume_claim_template = volume_source.volume_claim_template; + let annotations = volume_claim_template + .as_ref() + .and_then(|template| template.metadata.as_ref()) + .and_then(|metadata| metadata.annotations.as_ref()) + .cloned() + .unwrap_or_default(); + let spec = volume_claim_template.unwrap_or_default().spec; + let access_modes = spec.access_modes.unwrap_or_default(); + let requests = spec + .resources + .and_then(|resources| resources.requests) + .unwrap_or_default(); + + assert_eq!(1, annotations.len()); + assert_eq!( + Some(( + &"listeners.stackable.tech/listener-class".to_string(), + &"public".to_string() + )), + annotations.iter().next() + ); + assert_eq!( + Some("listeners.stackable.tech".to_string()), + spec.storage_class_name + ); + assert_eq!(1, access_modes.len()); + assert_eq!(Some(&"ReadWriteMany".to_string()), access_modes.first()); + assert_eq!(1, requests.len()); + assert_eq!( + Some((&"storage".to_string(), &Quantity("1".into()))), + requests.iter().next() + ); + } } From 114a5b1d6749d7bea74ba3733c9752f60739d70d Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Tue, 25 Oct 2022 16:19:40 +0200 Subject: [PATCH 2/5] Remove unused import --- src/builder/pod/volume.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/builder/pod/volume.rs b/src/builder/pod/volume.rs index 83a1c916b..9dfe34d5a 100644 --- a/src/builder/pod/volume.rs +++ b/src/builder/pod/volume.rs @@ -13,7 +13,6 @@ use k8s_openapi::{ use std::collections::BTreeMap; use crate::builder::ObjectMetaBuilder; -use crate::commons::listener; /// A builder to build [`Volume`] objects. /// May only contain one `volume_source` at a time. From 2d8553b555d4601799525cd0e5d0a1332629e8af Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Wed, 26 Oct 2022 13:07:57 +0200 Subject: [PATCH 3/5] Add shortcuts for creating listener volumes --- src/builder/pod/mod.rs | 42 +++++++++++++++++++++++++++++++++++++++ src/builder/pod/volume.rs | 25 +++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/builder/pod/mod.rs b/src/builder/pod/mod.rs index ab4687251..22369bdb0 100644 --- a/src/builder/pod/mod.rs +++ b/src/builder/pod/mod.rs @@ -15,6 +15,8 @@ use k8s_openapi::{ }; use std::collections::BTreeMap; +use super::{ListenerOperatorVolumeSourceBuilder, ListenerReference}; + /// A builder to build [`Pod`] objects. /// #[derive(Clone, Default)] @@ -146,6 +148,46 @@ impl PodBuilder { self } + /// Add a [`Volume`] for the storage class `listeners.stackable.tech` with the given listener + /// class. + pub fn add_listener_volume_by_listener_class( + &mut self, + volume_name: &str, + listener_class: &str, + ) -> &mut Self { + self.add_volume(Volume { + name: volume_name.into(), + ephemeral: Some( + ListenerOperatorVolumeSourceBuilder::new(&ListenerReference::ListenerClass( + listener_class.into(), + )) + .build(), + ), + ..Volume::default() + }); + self + } + + /// Add a [`Volume`] for the storage class `listeners.stackable.tech` with the given listener + /// name. + pub fn add_listener_volume_by_listener_name( + &mut self, + volume_name: &str, + listener_name: &str, + ) -> &mut Self { + self.add_volume(Volume { + name: volume_name.into(), + ephemeral: Some( + ListenerOperatorVolumeSourceBuilder::new(&ListenerReference::ListenerName( + listener_name.into(), + )) + .build(), + ), + ..Volume::default() + }); + self + } + pub fn image_pull_secrets( &mut self, secrets: impl IntoIterator + Iterator, diff --git a/src/builder/pod/volume.rs b/src/builder/pod/volume.rs index 9dfe34d5a..7747b1f86 100644 --- a/src/builder/pod/volume.rs +++ b/src/builder/pod/volume.rs @@ -363,6 +363,31 @@ impl ListenerReference { } /// Builder for an [`EphemeralVolumeSource`] containing the listener configuration +/// +/// # Example +/// +/// ``` +/// # use k8s_openapi::api::core::v1::Volume; +/// # use stackable_operator::builder::ListenerReference; +/// # use stackable_operator::builder::ListenerOperatorVolumeSourceBuilder; +/// # use stackable_operator::builder::PodBuilder; +/// let mut pod_builder = PodBuilder::new(); +/// +/// let volume_source = ListenerOperatorVolumeSourceBuilder::new( +/// &ListenerReference::ListenerClass("nodeport".into()), +/// ) +/// .build(); +/// pod_builder +/// .add_volume(Volume { +/// name: "listener".to_string(), +/// ephemeral: Some(volume_source), +/// ..Volume::default() +/// }); +/// +/// // There is also a shortcut for the code above: +/// pod_builder +/// .add_listener_volume_by_listener_class("listener", "nodeport"); +/// ``` #[derive(Clone, Debug, Eq, PartialEq)] pub struct ListenerOperatorVolumeSourceBuilder { listener_reference: ListenerReference, From c3b34d37dbc0ed61ad0153ae538700ebee68a017 Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Wed, 26 Oct 2022 16:14:13 +0200 Subject: [PATCH 4/5] Add examples to the documentation of add_listener_volume_by_listener_class/name --- src/builder/pod/mod.rs | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/builder/pod/mod.rs b/src/builder/pod/mod.rs index 22369bdb0..b14b6c410 100644 --- a/src/builder/pod/mod.rs +++ b/src/builder/pod/mod.rs @@ -150,6 +150,41 @@ impl PodBuilder { /// Add a [`Volume`] for the storage class `listeners.stackable.tech` with the given listener /// class. + /// + /// # Example + /// + /// ``` + /// # use stackable_operator::builder::PodBuilder; + /// let pod = PodBuilder::new() + /// .metadata_default() + /// .add_listener_volume_by_listener_class("listener", "nodeport") + /// .build() + /// .unwrap(); + /// + /// assert_eq!("\ + /// apiVersion: v1 + /// kind: Pod + /// metadata: {} + /// spec: + /// affinity: {} + /// containers: [] + /// enableServiceLinks: false + /// volumes: + /// - ephemeral: + /// volumeClaimTemplate: + /// metadata: + /// annotations: + /// listeners.stackable.tech/listener-class: nodeport + /// spec: + /// accessModes: + /// - ReadWriteMany + /// resources: + /// requests: + /// storage: '1' + /// storageClassName: listeners.stackable.tech + /// name: listener + /// ", serde_yaml::to_string(&pod).unwrap()) + /// ``` pub fn add_listener_volume_by_listener_class( &mut self, volume_name: &str, @@ -170,6 +205,41 @@ impl PodBuilder { /// Add a [`Volume`] for the storage class `listeners.stackable.tech` with the given listener /// name. + /// + /// # Example + /// + /// ``` + /// # use stackable_operator::builder::PodBuilder; + /// let pod = PodBuilder::new() + /// .metadata_default() + /// .add_listener_volume_by_listener_name("listener", "preprovisioned-listener") + /// .build() + /// .unwrap(); + /// + /// assert_eq!("\ + /// apiVersion: v1 + /// kind: Pod + /// metadata: {} + /// spec: + /// affinity: {} + /// containers: [] + /// enableServiceLinks: false + /// volumes: + /// - ephemeral: + /// volumeClaimTemplate: + /// metadata: + /// annotations: + /// listeners.stackable.tech/listener-name: preprovisioned-listener + /// spec: + /// accessModes: + /// - ReadWriteMany + /// resources: + /// requests: + /// storage: '1' + /// storageClassName: listeners.stackable.tech + /// name: listener + /// ", serde_yaml::to_string(&pod).unwrap()) + /// ``` pub fn add_listener_volume_by_listener_name( &mut self, volume_name: &str, From deee0995e4a770d3132d1a8d9b10a0c126e76e07 Mon Sep 17 00:00:00 2001 From: Siegfried Weber Date: Wed, 26 Oct 2022 16:34:51 +0200 Subject: [PATCH 5/5] Extend documentation with volume mounts for the listeners --- src/builder/pod/mod.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/builder/pod/mod.rs b/src/builder/pod/mod.rs index b14b6c410..34d338057 100644 --- a/src/builder/pod/mod.rs +++ b/src/builder/pod/mod.rs @@ -155,8 +155,15 @@ impl PodBuilder { /// /// ``` /// # use stackable_operator::builder::PodBuilder; + /// # use stackable_operator::builder::ContainerBuilder; /// let pod = PodBuilder::new() /// .metadata_default() + /// .add_container( + /// ContainerBuilder::new("container") + /// .unwrap() + /// .add_volume_mount("listener", "/path/to/volume") + /// .build(), + /// ) /// .add_listener_volume_by_listener_class("listener", "nodeport") /// .build() /// .unwrap(); @@ -167,7 +174,11 @@ impl PodBuilder { /// metadata: {} /// spec: /// affinity: {} - /// containers: [] + /// containers: + /// - name: container + /// volumeMounts: + /// - mountPath: /path/to/volume + /// name: listener /// enableServiceLinks: false /// volumes: /// - ephemeral: @@ -210,8 +221,15 @@ impl PodBuilder { /// /// ``` /// # use stackable_operator::builder::PodBuilder; + /// # use stackable_operator::builder::ContainerBuilder; /// let pod = PodBuilder::new() /// .metadata_default() + /// .add_container( + /// ContainerBuilder::new("container") + /// .unwrap() + /// .add_volume_mount("listener", "/path/to/volume") + /// .build(), + /// ) /// .add_listener_volume_by_listener_name("listener", "preprovisioned-listener") /// .build() /// .unwrap(); @@ -222,7 +240,11 @@ impl PodBuilder { /// metadata: {} /// spec: /// affinity: {} - /// containers: [] + /// containers: + /// - name: container + /// volumeMounts: + /// - mountPath: /path/to/volume + /// name: listener /// enableServiceLinks: false /// volumes: /// - ephemeral: