From 282a9750c3dbc524342aed1ed75c110c30c404ec Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 19 Dec 2025 13:15:26 +0100 Subject: [PATCH 1/5] chore: Let rustfmt format code in doc comments --- crates/k8s-version/src/lib.rs | 7 +- crates/stackable-operator-derive/src/lib.rs | 10 ++- crates/stackable-operator/src/builder/mod.rs | 1 - .../stackable-operator/src/builder/pod/mod.rs | 32 +++++---- .../src/builder/pod/probe.rs | 5 +- .../src/builder/pod/volume.rs | 27 ++++---- crates/stackable-operator/src/cli/mod.rs | 8 +-- crates/stackable-operator/src/client.rs | 23 +++---- .../src/cluster_resources.rs | 69 ++++++++++--------- crates/stackable-operator/src/commons/opa.rs | 25 ++++--- .../src/commons/resources.rs | 14 ++-- crates/stackable-operator/src/config/merge.rs | 11 +-- .../src/kvp/annotation/mod.rs | 3 +- crates/stackable-operator/src/kvp/key.rs | 2 + .../stackable-operator/src/kvp/label/mod.rs | 3 +- .../src/product_config_utils.rs | 16 ++--- .../src/product_logging/framework.rs | 65 +++++------------ .../src/product_logging/spec.rs | 5 +- .../src/status/condition/mod.rs | 23 +++---- .../src/instrumentation/axum/mod.rs | 2 +- crates/stackable-telemetry/src/tracing/mod.rs | 38 +++++----- crates/stackable-versioned-macros/src/lib.rs | 58 +++++----------- crates/stackable-webhook/src/lib.rs | 6 +- crates/stackable-webhook/src/options.rs | 8 +-- .../src/servers/conversion.rs | 43 ++++++------ rustfmt.toml | 1 + 26 files changed, 230 insertions(+), 275 deletions(-) diff --git a/crates/k8s-version/src/lib.rs b/crates/k8s-version/src/lib.rs index 2a620d592..a384742f0 100644 --- a/crates/k8s-version/src/lib.rs +++ b/crates/k8s-version/src/lib.rs @@ -28,7 +28,7 @@ //! //! ``` //! # use std::str::FromStr; -//! use k8s_version::{ApiVersion, Version, Level, Group}; +//! use k8s_version::{ApiVersion, Group, Level, Version}; //! //! let version = Version::new(1, Some(Level::Beta(1))); //! let group = Group::from_str("extension").unwrap(); @@ -38,10 +38,7 @@ //! //! // Or using ::try_new() //! let version = Version::new(1, Some(Level::Beta(1))); -//! let api_version = ApiVersion::try_new( -//! Some("extension"), -//! version -//! ).unwrap(); +//! let api_version = ApiVersion::try_new(Some("extension"), version).unwrap(); //! //! assert_eq!(api_version.to_string(), "extension/v1beta1"); //! ``` diff --git a/crates/stackable-operator-derive/src/lib.rs b/crates/stackable-operator-derive/src/lib.rs index d847de3c6..bf7dfe582 100644 --- a/crates/stackable-operator-derive/src/lib.rs +++ b/crates/stackable-operator-derive/src/lib.rs @@ -21,7 +21,10 @@ mod merge; /// # use stackable_operator::config::merge::Merge; /// #[derive(Merge)] /// #[merge(bound = "T: Merge")] -/// struct Wrapper where T: Clone { +/// struct Wrapper +/// where +/// T: Clone, +/// { /// inner: T, /// } /// ``` @@ -30,7 +33,10 @@ mod merge; /// /// ``` /// # use stackable_operator::config::merge::Merge; -/// struct Wrapper where T: Clone { +/// struct Wrapper +/// where +/// T: Clone, +/// { /// inner: T, /// } /// impl Merge for Wrapper diff --git a/crates/stackable-operator/src/builder/mod.rs b/crates/stackable-operator/src/builder/mod.rs index e4c619c17..1166dbbfb 100644 --- a/crates/stackable-operator/src/builder/mod.rs +++ b/crates/stackable-operator/src/builder/mod.rs @@ -2,7 +2,6 @@ //! //! They are often not _pure_ builders but contain extra logic to set fields based on others or //! to fill in sensible defaults. -//! pub mod configmap; pub mod event; pub mod meta; diff --git a/crates/stackable-operator/src/builder/pod/mod.rs b/crates/stackable-operator/src/builder/pod/mod.rs index 143f7bb13..97b214c12 100644 --- a/crates/stackable-operator/src/builder/pod/mod.rs +++ b/crates/stackable-operator/src/builder/pod/mod.rs @@ -337,10 +337,11 @@ impl PodBuilder { /// # }; /// # use std::collections::BTreeMap; /// - /// let labels: Labels = Labels::try_from( - /// BTreeMap::from([("app.kubernetes.io/component", "test-role"), - /// ("app.kubernetes.io/instance", "test"), - /// ("app.kubernetes.io/name", "test")])) + /// let labels: Labels = Labels::try_from(BTreeMap::from([ + /// ("app.kubernetes.io/component", "test-role"), + /// ("app.kubernetes.io/instance", "test"), + /// ("app.kubernetes.io/name", "test"), + /// ])) /// .unwrap(); /// /// let pod = PodBuilder::new() @@ -357,7 +358,8 @@ impl PodBuilder { /// .build() /// .unwrap(); /// - /// assert_eq!("\ + /// assert_eq!( + /// "\ /// apiVersion: v1 /// kind: Pod /// metadata: {} @@ -387,7 +389,9 @@ impl PodBuilder { /// storage: '1' /// storageClassName: listeners.stackable.tech /// name: listener - /// ", serde_yaml::to_string(&pod).unwrap()) + /// ", + /// serde_yaml::to_string(&pod).unwrap() + /// ) /// ``` pub fn add_listener_volume_by_listener_class( &mut self, @@ -423,10 +427,11 @@ impl PodBuilder { /// # }; /// # use std::collections::BTreeMap; /// - /// let labels: Labels = Labels::try_from( - /// BTreeMap::from([("app.kubernetes.io/component", "test-role"), - /// ("app.kubernetes.io/instance", "test"), - /// ("app.kubernetes.io/name", "test")])) + /// let labels: Labels = Labels::try_from(BTreeMap::from([ + /// ("app.kubernetes.io/component", "test-role"), + /// ("app.kubernetes.io/instance", "test"), + /// ("app.kubernetes.io/name", "test"), + /// ])) /// .unwrap(); /// /// let pod = PodBuilder::new() @@ -443,7 +448,8 @@ impl PodBuilder { /// .build() /// .unwrap(); /// - /// assert_eq!("\ + /// assert_eq!( + /// "\ /// apiVersion: v1 /// kind: Pod /// metadata: {} @@ -473,7 +479,9 @@ impl PodBuilder { /// storage: '1' /// storageClassName: listeners.stackable.tech /// name: listener - /// ", serde_yaml::to_string(&pod).unwrap()) + /// ", + /// serde_yaml::to_string(&pod).unwrap() + /// ) /// ``` pub fn add_listener_volume_by_listener_name( &mut self, diff --git a/crates/stackable-operator/src/builder/pod/probe.rs b/crates/stackable-operator/src/builder/pod/probe.rs index 6bb4ebd08..2915bf1eb 100644 --- a/crates/stackable-operator/src/builder/pod/probe.rs +++ b/crates/stackable-operator/src/builder/pod/probe.rs @@ -7,10 +7,7 @@ //! ### Usage example //! //! ``` -//! use stackable_operator::{ -//! builder::pod::probe::ProbeBuilder, -//! shared::time::Duration, -//! }; +//! use stackable_operator::{builder::pod::probe::ProbeBuilder, shared::time::Duration}; //! # use k8s_openapi::api::core::v1::HTTPGetAction; //! # use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; //! diff --git a/crates/stackable-operator/src/builder/pod/volume.rs b/crates/stackable-operator/src/builder/pod/volume.rs index 0175a11d4..9b7a1bd98 100644 --- a/crates/stackable-operator/src/builder/pod/volume.rs +++ b/crates/stackable-operator/src/builder/pod/volume.rs @@ -466,24 +466,21 @@ pub enum ListenerOperatorVolumeSourceBuilderError { /// /// let labels: Labels = Labels::try_from(BTreeMap::::new()).unwrap(); /// -/// let volume_source = -/// ListenerOperatorVolumeSourceBuilder::new( -/// &ListenerReference::ListenerClass("nodeport".into()), -/// &labels, -/// ) -/// .build_ephemeral() -/// .unwrap(); +/// let volume_source = ListenerOperatorVolumeSourceBuilder::new( +/// &ListenerReference::ListenerClass("nodeport".into()), +/// &labels, +/// ) +/// .build_ephemeral() +/// .unwrap(); /// -/// pod_builder -/// .add_volume(Volume { -/// name: "listener".to_string(), -/// ephemeral: Some(volume_source), -/// ..Volume::default() -/// }); +/// 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", &labels); +/// pod_builder.add_listener_volume_by_listener_class("listener", "nodeport", &labels); /// ``` #[derive(Clone, Debug)] pub struct ListenerOperatorVolumeSourceBuilder { diff --git a/crates/stackable-operator/src/cli/mod.rs b/crates/stackable-operator/src/cli/mod.rs index f11a37422..0224604fc 100644 --- a/crates/stackable-operator/src/cli/mod.rs +++ b/crates/stackable-operator/src/cli/mod.rs @@ -23,8 +23,8 @@ pub const AUTHOR: &str = "Stackable GmbH - info@stackable.tech"; /// [`RunArguments`] is used, but a custom type can be used. /// /// ```rust -/// use stackable_operator::cli::Command; /// use clap::Parser; +/// use stackable_operator::cli::Command; /// /// #[derive(Parser)] /// struct Run { @@ -39,8 +39,8 @@ pub const AUTHOR: &str = "Stackable GmbH - info@stackable.tech"; /// enum. /// /// ```rust -/// use stackable_operator::cli::Command; /// use clap::Parser; +/// use stackable_operator::cli::Command; /// /// #[derive(Parser)] /// enum CustomCommand { @@ -48,7 +48,7 @@ pub const AUTHOR: &str = "Stackable GmbH - info@stackable.tech"; /// Hello, /// /// #[clap(flatten)] -/// Framework(Command) +/// Framework(Command), /// } /// ``` #[derive(Debug, PartialEq, Eq, Parser)] @@ -65,8 +65,8 @@ pub enum Command { /// ### Embed into an extended argument set /// /// ```rust -/// use stackable_operator::cli::RunArguments; /// use clap::Parser; +/// use stackable_operator::cli::RunArguments; /// /// #[derive(clap::Parser, Debug, PartialEq, Eq)] /// struct Run { diff --git a/crates/stackable-operator/src/client.rs b/crates/stackable-operator/src/client.rs index b910eaba9..bb9ceb122 100644 --- a/crates/stackable-operator/src/client.rs +++ b/crates/stackable-operator/src/client.rs @@ -542,33 +542,32 @@ impl Client { /// /// ```no_run /// use std::time::Duration; + /// /// use clap::Parser; - /// use tokio::time::error::Elapsed; - /// use kube::runtime::watcher; /// use k8s_openapi::api::core::v1::Pod; + /// use kube::runtime::watcher; /// use stackable_operator::{ /// client::{Client, initialize_operator}, /// utils::cluster_info::KubernetesClusterInfoOptions, /// }; + /// use tokio::time::error::Elapsed; /// - /// #[tokio::main] - /// async fn main() { + /// # async fn docs() { /// let cluster_info_options = KubernetesClusterInfoOptions::parse(); /// let client = initialize_operator(None, &cluster_info_options) /// .await /// .expect("Unable to construct client."); /// let watcher_config: watcher::Config = - /// watcher::Config::default().fields(&format!("metadata.name=nonexistent-pod")); + /// watcher::Config::default().fields(&format!("metadata.name=nonexistent-pod")); /// /// // Will time out in 1 second unless the nonexistent-pod actually exists - /// let wait_created_result: Result<(), Elapsed> = tokio::time::timeout( - /// Duration::from_secs(1), - /// client.wait_created::(&client.default_namespace, watcher_config), - /// ) - /// .await; - /// } + /// let wait_created_result: Result<(), Elapsed> = tokio::time::timeout( + /// Duration::from_secs(1), + /// client.wait_created::(&client.default_namespace, watcher_config), + /// ) + /// .await; + /// # } /// ``` - /// pub async fn wait_created(&self, namespace: &T::Namespace, watcher_config: watcher::Config) where T: Resource + GetApi + Clone + Debug + DeserializeOwned + Send + 'static, diff --git a/crates/stackable-operator/src/cluster_resources.rs b/crates/stackable-operator/src/cluster_resources.rs index c7f5e452b..da3ee1bcf 100644 --- a/crates/stackable-operator/src/cluster_resources.rs +++ b/crates/stackable-operator/src/cluster_resources.rs @@ -328,19 +328,26 @@ impl ClusterResource for Deployment { /// # Examples /// /// ``` -/// use k8s_openapi::api::apps::v1::StatefulSet; -/// use k8s_openapi::api::core::v1::{ConfigMap, Service}; -/// use kube::CustomResource; -/// use kube::core::{Resource, CustomResourceExt}; -/// use kube::runtime::controller::Action; +/// use std::sync::Arc; +/// +/// use k8s_openapi::api::{ +/// apps::v1::StatefulSet, +/// core::v1::{ConfigMap, Service}, +/// }; +/// use kube::{ +/// CustomResource, +/// core::{CustomResourceExt, Resource}, +/// runtime::controller::Action, +/// }; /// use schemars::JsonSchema; /// use serde::{Deserialize, Serialize}; -/// use stackable_operator::client::Client; -/// use stackable_operator::cluster_resources::{self, ClusterResourceApplyStrategy, ClusterResources}; -/// use stackable_operator::deep_merger::ObjectOverrides; -/// use stackable_operator::product_config_utils::ValidatedRoleConfigByPropertyKind; -/// use stackable_operator::role_utils::Role; -/// use std::sync::Arc; +/// use stackable_operator::{ +/// client::Client, +/// cluster_resources::{self, ClusterResourceApplyStrategy, ClusterResources}, +/// deep_merger::ObjectOverrides, +/// product_config_utils::ValidatedRoleConfigByPropertyKind, +/// role_utils::Role, +/// }; /// /// const APP_NAME: &str = "app"; /// const OPERATOR_NAME: &str = "app.stackable.tech"; @@ -352,7 +359,7 @@ impl ClusterResource for Deployment { /// version = "v1", /// kind = "AppCluster", /// plural = "AppClusters", -/// namespaced, +/// namespaced /// )] /// struct AppClusterSpec { /// #[serde(default)] @@ -360,15 +367,9 @@ impl ClusterResource for Deployment { /// } /// /// enum Error { -/// CreateClusterResources { -/// source: cluster_resources::Error, -/// }, -/// AddClusterResource { -/// source: cluster_resources::Error, -/// }, -/// DeleteOrphanedClusterResources { -/// source: cluster_resources::Error, -/// }, +/// CreateClusterResources { source: cluster_resources::Error }, +/// AddClusterResource { source: cluster_resources::Error }, +/// DeleteOrphanedClusterResources { source: cluster_resources::Error }, /// }; /// /// async fn reconcile(app: Arc, client: Arc) -> Result { @@ -385,35 +386,38 @@ impl ClusterResource for Deployment { /// .map_err(|source| Error::CreateClusterResources { source })?; /// /// let role_service = Service::default(); -/// let patched_role_service = -/// cluster_resources.add(&client, role_service) -/// .await -/// .map_err(|source| Error::AddClusterResource { source })?; +/// let patched_role_service = cluster_resources +/// .add(&client, role_service) +/// .await +/// .map_err(|source| Error::AddClusterResource { source })?; /// /// for (role_name, group_config) in validated_config.iter() { /// for (rolegroup_name, rolegroup_config) in group_config.iter() { /// let rolegroup_service = Service::default(); -/// cluster_resources.add(&client, rolegroup_service) +/// cluster_resources +/// .add(&client, rolegroup_service) /// .await /// .map_err(|source| Error::AddClusterResource { source })?; /// /// let rolegroup_configmap = ConfigMap::default(); -/// cluster_resources.add(&client, rolegroup_configmap) +/// cluster_resources +/// .add(&client, rolegroup_configmap) /// .await /// .map_err(|source| Error::AddClusterResource { source })?; /// /// let rolegroup_statefulset = StatefulSet::default(); -/// cluster_resources.add(&client, rolegroup_statefulset) +/// cluster_resources +/// .add(&client, rolegroup_statefulset) /// .await /// .map_err(|source| Error::AddClusterResource { source })?; /// } /// } /// /// let discovery_configmap = ConfigMap::default(); -/// let patched_discovery_configmap = -/// cluster_resources.add(&client, discovery_configmap) -/// .await -/// .map_err(|source| Error::AddClusterResource { source })?; +/// let patched_discovery_configmap = cluster_resources +/// .add(&client, discovery_configmap) +/// .await +/// .map_err(|source| Error::AddClusterResource { source })?; /// /// cluster_resources /// .delete_orphaned_resources(&client) @@ -666,7 +670,6 @@ impl ClusterResources { /// # Arguments /// /// * `client` - The client which is used to access Kubernetes - /// pub async fn delete_orphaned_resources(self, client: &Client) -> Result<()> { tokio::try_join!( self.delete_orphaned_resources_of_kind::(client), diff --git a/crates/stackable-operator/src/commons/opa.rs b/crates/stackable-operator/src/commons/opa.rs index 2eaa6044a..d41d47079 100644 --- a/crates/stackable-operator/src/commons/opa.rs +++ b/crates/stackable-operator/src/commons/opa.rs @@ -7,9 +7,11 @@ //! # Example //! ```rust //! use serde::{Deserialize, Serialize}; -//! use stackable_operator::kube::CustomResource; -//! use stackable_operator::commons::opa::{OpaApiVersion, OpaConfig}; -//! use stackable_operator::schemars::{self, JsonSchema}; +//! use stackable_operator::{ +//! commons::opa::{OpaApiVersion, OpaConfig}, +//! kube::CustomResource, +//! schemars::{self, JsonSchema}, +//! }; //! //! #[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] //! #[kube( @@ -18,11 +20,11 @@ //! kind = "TestCluster", //! plural = "testclusters", //! shortname = "test", -//! namespaced, +//! namespaced //! )] //! #[serde(rename_all = "camelCase")] //! pub struct TestClusterSpec { -//! opa: Option +//! opa: Option, //! } //! //! let cluster: TestCluster = serde_yaml::from_str( @@ -36,12 +38,19 @@ //! configMapName: simple-opa //! package: test //! ", -//! ).unwrap(); +//! ) +//! .unwrap(); //! //! let opa_config: &OpaConfig = cluster.spec.opa.as_ref().unwrap(); //! -//! assert_eq!(opa_config.document_url(&cluster, Some("allow"), OpaApiVersion::V1), "v1/data/test/allow".to_string()); -//! assert_eq!(opa_config.full_document_url(&cluster, "http://localhost:8081", None, OpaApiVersion::V1), "http://localhost:8081/v1/data/test".to_string()); +//! assert_eq!( +//! opa_config.document_url(&cluster, Some("allow"), OpaApiVersion::V1), +//! "v1/data/test/allow".to_string() +//! ); +//! assert_eq!( +//! opa_config.full_document_url(&cluster, "http://localhost:8081", None, OpaApiVersion::V1), +//! "http://localhost:8081/v1/data/test".to_string() +//! ); //! ``` use std::sync::LazyLock; diff --git a/crates/stackable-operator/src/commons/resources.rs b/crates/stackable-operator/src/commons/resources.rs index 4f3d1a208..c5d0553d0 100644 --- a/crates/stackable-operator/src/commons/resources.rs +++ b/crates/stackable-operator/src/commons/resources.rs @@ -21,12 +21,14 @@ //! # Example //! //! ``` -//! use stackable_operator::config::fragment::Fragment; -//! use stackable_operator::role_utils::Role; -//! use stackable_operator::commons::resources::{Resources, PvcConfig, JvmHeapLimits}; +//! use kube::CustomResource; //! use schemars::JsonSchema; //! use serde::{Deserialize, Serialize}; -//! use kube::CustomResource; +//! use stackable_operator::{ +//! commons::resources::{JvmHeapLimits, PvcConfig, Resources}, +//! config::fragment::Fragment, +//! role_utils::Role, +//! }; //! //! #[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, Serialize)] //! #[kube( @@ -50,7 +52,7 @@ //! #[derive(Debug, Default, PartialEq, Fragment, JsonSchema)] //! #[fragment_attrs( //! derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema), -//! serde(rename_all = "camelCase"), +//! serde(rename_all = "camelCase") //! )] //! pub struct ProductConfig { //! resources: Resources, @@ -59,7 +61,7 @@ //! #[derive(Debug, Default, PartialEq, Fragment, JsonSchema)] //! #[fragment_attrs( //! derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema), -//! serde(rename_all = "camelCase"), +//! serde(rename_all = "camelCase") //! )] //! pub struct ProductStorageConfig { //! data_storage: PvcConfig, diff --git a/crates/stackable-operator/src/config/merge.rs b/crates/stackable-operator/src/config/merge.rs index a4c334a73..865cec30d 100644 --- a/crates/stackable-operator/src/config/merge.rs +++ b/crates/stackable-operator/src/config/merge.rs @@ -38,10 +38,13 @@ use stackable_shared::time::Duration; /// bar: Some(1), /// baz: Some(2), /// }); -/// assert_eq!(config, Foo { -/// bar: Some(0), // Overridden by `bar: Some(0)` above -/// baz: Some(2), // Fallback is used -/// }); +/// assert_eq!( +/// config, +/// Foo { +/// bar: Some(0), // Overridden by `bar: Some(0)` above +/// baz: Some(2), // Fallback is used +/// } +/// ); /// ``` /// /// # Options diff --git a/crates/stackable-operator/src/kvp/annotation/mod.rs b/crates/stackable-operator/src/kvp/annotation/mod.rs index c73d64c44..efe7ec388 100644 --- a/crates/stackable-operator/src/kvp/annotation/mod.rs +++ b/crates/stackable-operator/src/kvp/annotation/mod.rs @@ -175,7 +175,8 @@ impl Annotation { /// let labels = Annotations::try_from([ /// ("stackable.tech/managed-by", "stackablectl"), /// ("stackable.tech/vendor", "Stäckable"), -/// ]).unwrap(); +/// ]) +/// .unwrap(); /// ``` #[derive(Clone, Debug, Default)] pub struct Annotations(KeyValuePairs); diff --git a/crates/stackable-operator/src/kvp/key.rs b/crates/stackable-operator/src/kvp/key.rs index 98ef7cb1d..3bc75671d 100644 --- a/crates/stackable-operator/src/kvp/key.rs +++ b/crates/stackable-operator/src/kvp/key.rs @@ -114,6 +114,7 @@ impl Key { /// /// ``` /// use std::str::FromStr; + /// /// use stackable_operator::kvp::{Key, KeyPrefix}; /// /// let key = Key::from_str("stackable.tech/vendor").unwrap(); @@ -144,6 +145,7 @@ impl Key { /// /// ``` /// use std::str::FromStr; + /// /// use stackable_operator::kvp::{Key, KeyName}; /// /// let key = Key::from_str("stackable.tech/vendor").unwrap(); diff --git a/crates/stackable-operator/src/kvp/label/mod.rs b/crates/stackable-operator/src/kvp/label/mod.rs index 2a83211fe..edae2fdbe 100644 --- a/crates/stackable-operator/src/kvp/label/mod.rs +++ b/crates/stackable-operator/src/kvp/label/mod.rs @@ -249,7 +249,8 @@ impl Label { /// let labels = Labels::try_from([ /// ("stackable.tech/managed-by", "stackablectl"), /// ("stackable.tech/vendor", "Stackable"), -/// ]).unwrap(); +/// ]) +/// .unwrap(); /// ``` #[derive(Clone, Debug, Default)] pub struct Labels(KeyValuePairs); diff --git a/crates/stackable-operator/src/product_config_utils.rs b/crates/stackable-operator/src/product_config_utils.rs index f78d580c9..a95879c1a 100644 --- a/crates/stackable-operator/src/product_config_utils.rs +++ b/crates/stackable-operator/src/product_config_utils.rs @@ -587,7 +587,9 @@ pub fn env_vars_from_rolegroup_config( /// /// ``` /// use k8s_openapi::api::core::v1::EnvVar; -/// use stackable_operator::{product_config_utils::env_vars_from, role_utils::CommonConfiguration}; +/// use stackable_operator::{ +/// product_config_utils::env_vars_from, role_utils::CommonConfiguration, +/// }; /// /// let common_config = CommonConfiguration::<(), ()> { /// env_overrides: [("VAR".to_string(), "value".to_string())] @@ -601,7 +603,7 @@ pub fn env_vars_from_rolegroup_config( /// let expected_env_vars = vec![EnvVar { /// name: "VAR".to_string(), /// value: Some("value".to_string()), -/// value_from: None +/// value_from: None, /// }]; /// /// assert_eq!(expected_env_vars, env_vars); @@ -651,14 +653,8 @@ pub fn env_var_from_tuple(entry: (impl Into, impl Into)) -> EnvV /// ``` /// use stackable_operator::product_config_utils::{env_vars_from, insert_or_update_env_vars}; /// -/// let env_vars = env_vars_from([ -/// ("VAR1", "original value 1"), -/// ("VAR2", "original value 2") -/// ]); -/// let env_overrides = env_vars_from([ -/// ("VAR2", "overriden value 2"), -/// ("VAR3", "new value 3") -/// ]); +/// let env_vars = env_vars_from([("VAR1", "original value 1"), ("VAR2", "original value 2")]); +/// let env_overrides = env_vars_from([("VAR2", "overriden value 2"), ("VAR3", "new value 3")]); /// /// let combined_env_vars = insert_or_update_env_vars(&env_vars, &env_overrides); /// diff --git a/crates/stackable-operator/src/product_logging/framework.rs b/crates/stackable-operator/src/product_logging/framework.rs index 0279a23a8..62e6e5de0 100644 --- a/crates/stackable-operator/src/product_logging/framework.rs +++ b/crates/stackable-operator/src/product_logging/framework.rs @@ -68,14 +68,8 @@ pub enum LoggingError { /// /// ``` /// use stackable_operator::{ -/// builder::{ -/// pod::PodBuilder, -/// meta::ObjectMetaBuilder, -/// }, -/// memory::{ -/// BinaryMultiple, -/// MemoryQuantity, -/// }, +/// builder::{meta::ObjectMetaBuilder, pod::PodBuilder}, +/// memory::{BinaryMultiple, MemoryQuantity}, /// }; /// # use stackable_operator::product_logging; /// @@ -128,9 +122,7 @@ pub fn calculate_log_volume_size_limit(max_log_files_size: &[MemoryQuantity]) -> /// builder::pod::container::ContainerBuilder, /// config::fragment, /// product_logging, -/// product_logging::spec::{ -/// ContainerLogConfig, ContainerLogConfigChoice, Logging, -/// }, +/// product_logging::spec::{ContainerLogConfig, ContainerLogConfigChoice, Logging}, /// }; /// # use stackable_operator::product_logging::spec::default_logging; /// # use strum::{Display, EnumIter}; @@ -239,15 +231,10 @@ pub fn capture_shell_output( /// /// ``` /// use stackable_operator::{ -/// builder::{ -/// configmap::ConfigMapBuilder, -/// meta::ObjectMetaBuilder, -/// }, +/// builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder}, /// config::fragment, /// product_logging, -/// product_logging::spec::{ -/// ContainerLogConfig, ContainerLogConfigChoice, Logging, -/// }, +/// product_logging::spec::{ContainerLogConfig, ContainerLogConfigChoice, Logging}, /// }; /// # use stackable_operator::product_logging::spec::default_logging; /// # use strum::{Display, EnumIter}; @@ -360,15 +347,10 @@ log4j.appender.FILE.layout=org.apache.log4j.xml.XMLLayout /// /// ``` /// use stackable_operator::{ -/// builder::{ -/// configmap::ConfigMapBuilder, -/// meta::ObjectMetaBuilder, -/// }, +/// builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder}, /// config::fragment, /// product_logging, -/// product_logging::spec::{ -/// ContainerLogConfig, ContainerLogConfigChoice, Logging, -/// }, +/// product_logging::spec::{ContainerLogConfig, ContainerLogConfigChoice, Logging}, /// }; /// # use stackable_operator::product_logging::spec::default_logging; /// # use strum::{Display, EnumIter}; @@ -507,14 +489,9 @@ rootLogger.appenderRef.FILE.ref = FILE"#, /// /// ``` /// use stackable_operator::{ -/// builder::{ -/// configmap::ConfigMapBuilder, -/// meta::ObjectMetaBuilder, -/// }, +/// builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder}, /// product_logging, -/// product_logging::spec::{ -/// ContainerLogConfig, ContainerLogConfigChoice, Logging, -/// }, +/// product_logging::spec::{ContainerLogConfig, ContainerLogConfigChoice, Logging}, /// }; /// # use stackable_operator::{ /// # config::fragment, @@ -534,7 +511,8 @@ rootLogger.appenderRef.FILE.ref = FILE"#, /// const MY_PRODUCT_LOG_FILE: &str = "my-product.log4j.xml"; /// const MAX_LOG_FILE_SIZE_IN_MIB: u32 = 10; /// const CONSOLE_CONVERSION_PATTERN: &str = "%d{ISO8601} %-5p %m%n"; -/// const ADDITIONAL_CONFIG: &str = " "; +/// const ADDITIONAL_CONFIG: &str = +/// " "; /// /// let mut cm_builder = ConfigMapBuilder::new(); /// cm_builder.metadata(ObjectMetaBuilder::default().build()); @@ -551,7 +529,7 @@ rootLogger.appenderRef.FILE.ref = FILE"#, /// MAX_LOG_FILE_SIZE_IN_MIB, /// CONSOLE_CONVERSION_PATTERN, /// log_config, -/// Some(ADDITIONAL_CONFIG) +/// Some(ADDITIONAL_CONFIG), /// ), /// ); /// } @@ -658,14 +636,9 @@ pub fn create_logback_config( /// /// ``` /// use stackable_operator::{ -/// builder::{ -/// configmap::ConfigMapBuilder, -/// meta::ObjectMetaBuilder, -/// }, +/// builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder}, /// product_logging, -/// product_logging::spec::{ -/// ContainerLogConfig, ContainerLogConfigChoice, Logging, -/// }, +/// product_logging::spec::{ContainerLogConfig, ContainerLogConfigChoice, Logging}, /// }; /// # use stackable_operator::{ /// # config::fragment, @@ -703,10 +676,7 @@ pub fn create_logback_config( /// if logging.enable_vector_agent { /// cm_builder.add_data( /// product_logging::framework::VECTOR_CONFIG_FILE, -/// product_logging::framework::create_vector_config( -/// &role_group, -/// vector_log_config, -/// ), +/// product_logging::framework::create_vector_config(&role_group, vector_log_config), /// ); /// } /// @@ -1496,10 +1466,7 @@ kill $vector_pid /// # Example /// /// ``` -/// use stackable_operator::{ -/// builder::pod::container::ContainerBuilder, -/// product_logging, -/// }; +/// use stackable_operator::{builder::pod::container::ContainerBuilder, product_logging}; /// /// const STACKABLE_LOG_DIR: &str = "/stackable/log"; /// diff --git a/crates/stackable-operator/src/product_logging/spec.rs b/crates/stackable-operator/src/product_logging/spec.rs index 67e65ab04..0e0588174 100644 --- a/crates/stackable-operator/src/product_logging/spec.rs +++ b/crates/stackable-operator/src/product_logging/spec.rs @@ -17,10 +17,7 @@ use crate::config::{ /// /// ``` /// use serde::{Deserialize, Serialize}; -/// use stackable_operator::{ -/// product_logging, -/// schemars::JsonSchema, -/// }; +/// use stackable_operator::{product_logging, schemars::JsonSchema}; /// use strum::{Display, EnumIter}; /// /// #[derive( diff --git a/crates/stackable-operator/src/status/condition/mod.rs b/crates/stackable-operator/src/status/condition/mod.rs index ea4af5b9c..e30f727a8 100644 --- a/crates/stackable-operator/src/status/condition/mod.rs +++ b/crates/stackable-operator/src/status/condition/mod.rs @@ -34,13 +34,14 @@ pub trait ConditionBuilder { /// /// # Examples /// ``` -/// use stackable_operator::status::condition::daemonset::DaemonSetConditionBuilder; -/// use stackable_operator::status::condition::statefulset::StatefulSetConditionBuilder; /// use k8s_openapi::api::apps::v1::{DaemonSet, StatefulSet}; -/// use stackable_operator::status::condition::{ClusterCondition, ConditionBuilder, HasStatusCondition, compute_conditions}; +/// use stackable_operator::status::condition::{ +/// ClusterCondition, ConditionBuilder, HasStatusCondition, compute_conditions, +/// daemonset::DaemonSetConditionBuilder, statefulset::StatefulSetConditionBuilder, +/// }; /// /// struct ClusterStatus { -/// conditions: Vec +/// conditions: Vec, /// } /// /// impl HasStatusCondition for ClusterStatus { @@ -55,19 +56,17 @@ pub trait ConditionBuilder { /// let mut statefulset_condition_builder = StatefulSetConditionBuilder::default(); /// statefulset_condition_builder.add(StatefulSet::default()); /// -/// let old_status = ClusterStatus { -/// conditions: vec![] -/// }; +/// let old_status = ClusterStatus { conditions: vec![] }; /// /// let new_status = ClusterStatus { -/// conditions: compute_conditions(&old_status, +/// conditions: compute_conditions( +/// &old_status, /// &[ /// &daemonset_condition_builder as &dyn ConditionBuilder, -/// &statefulset_condition_builder as &dyn ConditionBuilder -/// ] -/// ) +/// &statefulset_condition_builder as &dyn ConditionBuilder, +/// ], +/// ), /// }; -/// /// ``` pub fn compute_conditions( resource: &T, diff --git a/crates/stackable-telemetry/src/instrumentation/axum/mod.rs b/crates/stackable-telemetry/src/instrumentation/axum/mod.rs index 574cb8bff..c258c2e13 100644 --- a/crates/stackable-telemetry/src/instrumentation/axum/mod.rs +++ b/crates/stackable-telemetry/src/instrumentation/axum/mod.rs @@ -56,8 +56,8 @@ const OTEL_TRACE_ID_TO: &str = "opentelemetry.trace_id.to"; /// ### Example with Axum /// /// ``` +/// use axum::{Router, routing::get}; /// use stackable_telemetry::AxumTraceLayer; -/// use axum::{routing::get, Router}; /// /// let trace_layer = AxumTraceLayer::new(); /// let router = Router::new() diff --git a/crates/stackable-telemetry/src/tracing/mod.rs b/crates/stackable-telemetry/src/tracing/mod.rs index 2e36e699f..2fb206868 100644 --- a/crates/stackable-telemetry/src/tracing/mod.rs +++ b/crates/stackable-telemetry/src/tracing/mod.rs @@ -83,12 +83,11 @@ pub enum Error { /// #[tokio::main] /// async fn main() -> Result<(), Error> { /// let _tracing_guard = Tracing::builder() // < Scope starts here -/// .service_name("test") // | -/// .build() // | -/// .init()?; // | -/// // | -/// tracing::info!("log a message"); // | -/// Ok(()) // < Scope ends here, guard is dropped +/// .service_name("test") +/// .build() +/// .init()?; +/// tracing::info!("log a message"); +/// Ok(()) // < Scope ends here, guard is dropped /// } /// ``` /// @@ -101,19 +100,19 @@ pub enum Error { /// defaults used. /// /// ``` -/// use stackable_telemetry::tracing::{Tracing, TelemetryOptions, Error}; +/// use stackable_telemetry::tracing::{Error, TelemetryOptions, Tracing}; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { /// let options = TelemetryOptions { -/// console_log_disabled: false, -/// console_log_format: Default::default(), -/// file_log_directory: None, -/// file_log_rotation_period: None, -/// file_log_max_files: Some(6), -/// otel_trace_exporter_enabled: true, -/// otel_log_exporter_enabled: true, -/// }; +/// console_log_disabled: false, +/// console_log_format: Default::default(), +/// file_log_directory: None, +/// file_log_rotation_period: None, +/// file_log_max_files: Some(6), +/// otel_trace_exporter_enabled: true, +/// otel_log_exporter_enabled: true, +/// }; /// /// let _tracing_guard = Tracing::pre_configured("test", options).init()?; /// @@ -142,7 +141,7 @@ pub enum Error { /// variable and default level can be set. /// /// ``` -/// use stackable_telemetry::tracing::{Tracing, Error, settings::Settings}; +/// use stackable_telemetry::tracing::{Error, Tracing, settings::Settings}; /// use tracing_subscriber::filter::LevelFilter; /// /// #[tokio::main] @@ -189,14 +188,14 @@ pub enum Error { /// Settings::builder() /// .with_environment_variable("CONSOLE_LOG") /// .with_default_level(LevelFilter::INFO) -/// .build() +/// .build(), /// ) /// .with_file_output( /// Settings::builder() /// .with_environment_variable("FILE_LOG") /// .with_default_level(LevelFilter::INFO) /// .file_log_settings_builder("/tmp/logs", "operator.log") -/// .build() +/// .build(), /// ) /// .with_otlp_log_exporter(otlp_log_flag.then(|| { /// Settings::builder() @@ -208,7 +207,7 @@ pub enum Error { /// Settings::builder() /// .with_environment_variable("OTLP_TRACE") /// .with_default_level(LevelFilter::TRACE) -/// .build() +/// .build(), /// ) /// .build() /// .init()?; @@ -777,7 +776,6 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into) -> /// /// Additionally, this struct can be used as operator CLI arguments. This functionality is only /// available if the feature `clap` is enabled. -/// #[cfg_attr( feature = "clap", doc = r#" diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 40124d688..43a7feccb 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -205,7 +205,6 @@ mod utils; /// #[versioned( /// version(name = "v1alpha1"), /// version(name = "v1beta1"), -/// /// crates( /// versioned = "::stackable_versioned", /// kube_client = "::kube::client", @@ -231,12 +230,12 @@ mod utils; /// #[versioned( /// version(name = "v1alpha1"), /// version(name = "v1beta1"), -/// +/// /// options(k8s( /// // Highly experimental conversion tracking. Opting into this feature will /// // introduce frequent breaking changes. /// experimental_conversion_tracking, -/// +/// /// // Enables instrumentation and log events via the tracing crate. /// enable_tracing, /// )) @@ -265,10 +264,7 @@ mod utils; /// # mod b { /// # pub mod v1alpha1 {} /// # } -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1"))] /// mod versioned { /// mod v1alpha1 { /// pub use a::v1alpha1::*; @@ -327,37 +323,34 @@ mod utils; /// # use schemars::JsonSchema; /// # use serde::{Deserialize, Serialize}; /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// #[versioned(crd( /// // **Required.** Set the group of the CRD, usually the domain of the /// // company, like `example.com`. /// group = "example.com", -/// +/// /// // Override the kind field of the CRD. This defaults to the struct /// // name (without the `Spec` suffix). Overriding this value will also /// // influence the names of other generated items, like the status /// // struct (if used) or the version enum. /// kind = "CustomKind", -/// +/// /// // Set the singular name. Defaults to lowercased `kind` value. /// singular = "...", -/// +/// /// // Set the plural name. Defaults to inferring from singular. /// plural = "...", -/// +/// /// // Indicate that this is a namespaced scoped resource rather than a /// // cluster scoped resource. /// namespaced, -/// +/// /// // Set the specified struct as the status subresource. If conversion /// // tracking is enabled, this struct will be automatically merged into /// // the generated tracking status struct. /// status = "FooStatus", -/// +/// /// // Set a shortname. This can be specified multiple times. /// shortname = "..." /// ))] @@ -406,10 +399,7 @@ mod utils; /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// pub struct Foo { /// #[versioned(added(since = "v1beta1"))] @@ -464,10 +454,7 @@ mod utils; /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// pub struct Foo { /// #[versioned(added(since = "v1beta1", default = "default_bar"))] @@ -523,10 +510,7 @@ mod utils; /// not fail. /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// pub struct Foo { /// #[versioned(changed( @@ -591,10 +575,7 @@ mod utils; /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// pub struct Foo { /// #[versioned(deprecated(since = "v1beta1"))] @@ -675,10 +656,7 @@ mod utils; /// # use kube::CustomResource; /// # use schemars::JsonSchema; /// # use serde::{Deserialize, Serialize}; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1") -/// )] +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] /// mod versioned { /// #[versioned(crd(group = "example.com"))] /// #[derive(Clone, Debug, Deserialize, Serialize, CustomResource, JsonSchema)] @@ -720,9 +698,10 @@ mod utils; /// # use serde::{Deserialize, Serialize}; /// # /// # #[versioned(version(name = "v1alpha1"))] -/// #[versioned(skip(merged_crd))] // Skip generation for ALL specs +/// #[versioned(skip(merged_crd))] // Skip generation for ALL specs /// mod versioned { /// #[versioned(skip(merged_crd))] // Skip generation for specific specs +/// /// # #[versioned(crd(group = "example.com"))] /// # #[derive(Clone, Debug, CustomResource, Deserialize, Serialize, JsonSchema)] /// pub struct FooSpec {} @@ -779,9 +758,10 @@ mod utils; /// # use serde::{Deserialize, Serialize}; /// # /// # #[versioned(version(name = "v1alpha1"))] -/// #[versioned(skip(try_convert))] // Skip generation for ALL specs +/// #[versioned(skip(try_convert))] // Skip generation for ALL specs /// mod versioned { /// #[versioned(skip(try_convert))] // Skip generation for specific specs +/// /// # #[versioned(crd(group = "example.com"))] /// # #[derive(Clone, Debug, CustomResource, Deserialize, Serialize, JsonSchema)] /// pub struct FooSpec {} diff --git a/crates/stackable-webhook/src/lib.rs b/crates/stackable-webhook/src/lib.rs index ee33ab542..ac365bcfb 100644 --- a/crates/stackable-webhook/src/lib.rs +++ b/crates/stackable-webhook/src/lib.rs @@ -8,8 +8,8 @@ //! routes and their handler functions. //! //! ``` -//! use stackable_webhook::{WebhookServer, WebhookOptions}; //! use axum::Router; +//! use stackable_webhook::{WebhookOptions, WebhookServer}; //! //! # async fn test() { //! let router = Router::new(); @@ -112,8 +112,8 @@ impl WebhookServer { /// ### Basic Example /// /// ``` - /// use stackable_webhook::{WebhookServer, WebhookOptions}; /// use axum::Router; + /// use stackable_webhook::{WebhookOptions, WebhookServer}; /// /// # async fn test() { /// let router = Router::new(); @@ -126,8 +126,8 @@ impl WebhookServer { /// ### Example with Custom Options /// /// ``` - /// use stackable_webhook::{WebhookServer, WebhookOptions}; /// use axum::Router; + /// use stackable_webhook::{WebhookOptions, WebhookServer}; /// /// # async fn test() { /// let options = WebhookOptions::builder() diff --git a/crates/stackable-webhook/src/options.rs b/crates/stackable-webhook/src/options.rs index b7eeaffea..ed713ebfc 100644 --- a/crates/stackable-webhook/src/options.rs +++ b/crates/stackable-webhook/src/options.rs @@ -27,14 +27,10 @@ use crate::WebhookServer; /// .build(); /// /// // Set IP address only -/// let options = WebhookOptions::builder() -/// .bind_ip([0, 0, 0, 0]) -/// .build(); +/// let options = WebhookOptions::builder().bind_ip([0, 0, 0, 0]).build(); /// /// // Set port only -/// let options = WebhookOptions::builder() -/// .bind_port(12345) -/// .build(); +/// let options = WebhookOptions::builder().bind_port(12345).build(); /// ``` #[derive(Debug)] pub struct WebhookOptions { diff --git a/crates/stackable-webhook/src/servers/conversion.rs b/crates/stackable-webhook/src/servers/conversion.rs index f15b4b0a3..be1f51b92 100644 --- a/crates/stackable-webhook/src/servers/conversion.rs +++ b/crates/stackable-webhook/src/servers/conversion.rs @@ -109,19 +109,17 @@ impl ConversionWebhookServer { /// /// ```no_run /// # use tokio_rustls::rustls::crypto::{CryptoProvider, ring::default_provider}; - /// use stackable_webhook::servers::{ConversionWebhookServer, ConversionWebhookOptions}; /// use stackable_operator::crd::s3::{S3Connection, S3ConnectionVersion}; + /// use stackable_webhook::servers::{ConversionWebhookOptions, ConversionWebhookServer}; /// /// # #[tokio::main] /// # async fn main() { /// # CryptoProvider::install_default(default_provider()).unwrap(); - /// let crds_and_handlers = vec![ - /// ( - /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) - /// .expect("the S3Connection CRD must be merged"), - /// S3Connection::try_convert, - /// ) - /// ]; + /// let crds_and_handlers = vec![( + /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) + /// .expect("the S3Connection CRD must be merged"), + /// S3Connection::try_convert, + /// )]; /// /// let options = ConversionWebhookOptions { /// socket_addr: ConversionWebhookServer::DEFAULT_SOCKET_ADDRESS, @@ -130,9 +128,9 @@ impl ConversionWebhookServer { /// }; /// /// let (conversion_webhook_server, _certificate_rx) = - /// ConversionWebhookServer::new(crds_and_handlers, options) - /// .await - /// .unwrap(); + /// ConversionWebhookServer::new(crds_and_handlers, options) + /// .await + /// .unwrap(); /// /// conversion_webhook_server.run().await.unwrap(); /// # } @@ -220,21 +218,22 @@ impl ConversionWebhookServer { /// ```no_run /// # use futures_util::TryFutureExt; /// # use tokio_rustls::rustls::crypto::{CryptoProvider, ring::default_provider}; - /// use stackable_webhook::servers::{ConversionWebhookServer, ConversionWebhookOptions}; - /// use stackable_operator::{kube::Client, crd::s3::{S3Connection, S3ConnectionVersion}}; + /// use stackable_operator::{ + /// crd::s3::{S3Connection, S3ConnectionVersion}, + /// kube::Client, + /// }; + /// use stackable_webhook::servers::{ConversionWebhookOptions, ConversionWebhookServer}; /// /// # #[tokio::main] /// # async fn main() { /// # CryptoProvider::install_default(default_provider()).unwrap(); /// let client = Client::try_default().await.unwrap(); /// - /// let crds_and_handlers = vec![ - /// ( - /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) - /// .expect("the S3Connection CRD must be merged"), - /// S3Connection::try_convert, - /// ) - /// ]; + /// let crds_and_handlers = vec![( + /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) + /// .expect("the S3Connection CRD must be merged"), + /// S3Connection::try_convert, + /// )]; /// /// let (conversion_webhook_server, crd_maintainer, _initial_reconcile_rx) = /// ConversionWebhookServer::with_maintainer( @@ -252,9 +251,7 @@ impl ConversionWebhookServer { /// .run() /// .map_err(|err| err.to_string()); /// - /// let crd_maintainer = crd_maintainer - /// .run() - /// .map_err(|err| err.to_string()); + /// let crd_maintainer = crd_maintainer.run().map_err(|err| err.to_string()); /// /// // Run both the conversion webhook server and crd_maintainer concurrently, eg. with /// // futures::try_join!. diff --git a/rustfmt.toml b/rustfmt.toml index 4731b22c8..7a787e469 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -4,3 +4,4 @@ imports_granularity = "Crate" group_imports = "StdExternalCrate" reorder_impl_items = true use_field_init_shorthand = true +format_code_in_doc_comments = true From 7690e04499e5a7b98b0448fdc0d9e0112aa85cf5 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 19 Dec 2025 15:52:55 +0100 Subject: [PATCH 2/5] restore old tracing fancy comments --- crates/stackable-telemetry/src/tracing/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/stackable-telemetry/src/tracing/mod.rs b/crates/stackable-telemetry/src/tracing/mod.rs index 2fb206868..4262c1f46 100644 --- a/crates/stackable-telemetry/src/tracing/mod.rs +++ b/crates/stackable-telemetry/src/tracing/mod.rs @@ -60,6 +60,7 @@ pub enum Error { }, } +#[rustfmt::skip] /// Easily initialize a set of pre-configured [`Subscriber`][1] layers. /// /// # Usage @@ -83,11 +84,12 @@ pub enum Error { /// #[tokio::main] /// async fn main() -> Result<(), Error> { /// let _tracing_guard = Tracing::builder() // < Scope starts here -/// .service_name("test") -/// .build() -/// .init()?; -/// tracing::info!("log a message"); -/// Ok(()) // < Scope ends here, guard is dropped +/// .service_name("test") // | +/// .build() // | +/// .init()?; // | +/// // | +/// tracing::info!("log a message"); // | +/// Ok(()) // < Scope ends here, guard is dropped /// } /// ``` /// From 7cf28554854eec8f66571d9d1b650cc194ae88b8 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Mon, 22 Dec 2025 08:53:46 +0100 Subject: [PATCH 3/5] Remove problematic empty lines rustfmt added trailing whitespaces, which collided with the trailing-whitespace check --- crates/stackable-versioned-macros/src/lib.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 43a7feccb..50476e713 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -230,12 +230,10 @@ mod utils; /// #[versioned( /// version(name = "v1alpha1"), /// version(name = "v1beta1"), -/// /// options(k8s( /// // Highly experimental conversion tracking. Opting into this feature will /// // introduce frequent breaking changes. /// experimental_conversion_tracking, -/// /// // Enables instrumentation and log events via the tracing crate. /// enable_tracing, /// )) @@ -329,28 +327,22 @@ mod utils; /// // **Required.** Set the group of the CRD, usually the domain of the /// // company, like `example.com`. /// group = "example.com", -/// /// // Override the kind field of the CRD. This defaults to the struct /// // name (without the `Spec` suffix). Overriding this value will also /// // influence the names of other generated items, like the status /// // struct (if used) or the version enum. /// kind = "CustomKind", -/// /// // Set the singular name. Defaults to lowercased `kind` value. /// singular = "...", -/// /// // Set the plural name. Defaults to inferring from singular. /// plural = "...", -/// /// // Indicate that this is a namespaced scoped resource rather than a /// // cluster scoped resource. /// namespaced, -/// /// // Set the specified struct as the status subresource. If conversion /// // tracking is enabled, this struct will be automatically merged into /// // the generated tracking status struct. /// status = "FooStatus", -/// /// // Set a shortname. This can be specified multiple times. /// shortname = "..." /// ))] From 99da9bbe9f77f57f65af239fd8e19c67d377798d Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Mon, 22 Dec 2025 15:15:21 +0100 Subject: [PATCH 4/5] fix merge conflicts --- crates/stackable-webhook/src/lib.rs | 10 +---- .../src/webhooks/conversion_webhook.rs | 43 ++++++++++--------- .../src/webhooks/mutating_webhook.rs | 36 ++++++++++------ 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/crates/stackable-webhook/src/lib.rs b/crates/stackable-webhook/src/lib.rs index b5ba1d375..43b16d816 100644 --- a/crates/stackable-webhook/src/lib.rs +++ b/crates/stackable-webhook/src/lib.rs @@ -3,15 +3,9 @@ //! //! Currently the following webhooks are supported: //! -<<<<<<< HEAD -//! ``` -//! use axum::Router; -//! use stackable_webhook::{WebhookOptions, WebhookServer}; -======= //! * [webhooks::ConversionWebhook] //! * [webhooks::MutatingWebhook] //! * In the future validating webhooks wil be added ->>>>>>> origin/main //! //! This library is fully compatible with the [`tracing`] crate and emits debug level tracing data. //! @@ -64,9 +58,7 @@ pub enum WebhookServerError { /// ### Example usage /// /// ``` -/// use stackable_webhook::WebhookServer; -/// use stackable_webhook::WebhookServerOptions; -/// use stackable_webhook::webhooks::Webhook; +/// use stackable_webhook::{WebhookServer, WebhookServerOptions, webhooks::Webhook}; /// /// # async fn docs() { /// let mut webhooks: Vec> = vec![]; diff --git a/crates/stackable-webhook/src/webhooks/conversion_webhook.rs b/crates/stackable-webhook/src/webhooks/conversion_webhook.rs index 0f5d8d918..31f099e5f 100644 --- a/crates/stackable-webhook/src/webhooks/conversion_webhook.rs +++ b/crates/stackable-webhook/src/webhooks/conversion_webhook.rs @@ -43,10 +43,17 @@ pub enum ConversionWebhookError { /// ``` /// use std::sync::Arc; /// -/// use stackable_operator::crd::s3::{S3Connection, S3ConnectionVersion}; -/// use stackable_operator::kube::{Client, core::admission::{AdmissionRequest, AdmissionResponse}}; -/// use stackable_webhook::WebhookServer; -/// use stackable_webhook::webhooks::{ConversionWebhook, ConversionWebhookOptions}; +/// use stackable_operator::{ +/// crd::s3::{S3Connection, S3ConnectionVersion}, +/// kube::{ +/// Client, +/// core::admission::{AdmissionRequest, AdmissionResponse}, +/// }, +/// }; +/// use stackable_webhook::{ +/// WebhookServer, +/// webhooks::{ConversionWebhook, ConversionWebhookOptions}, +/// }; /// /// # async fn docs() { /// // The Kubernetes client @@ -54,29 +61,23 @@ pub enum ConversionWebhookError { /// // Read in from user input, e.g. CLI arguments /// let disable_crd_maintenance = false; /// -/// let crds_and_handlers = vec![ -/// ( -/// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) -/// .expect("the S3Connection CRD must be merged"), -/// S3Connection::try_convert, -/// ) -/// ]; +/// let crds_and_handlers = vec![( +/// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) +/// .expect("the S3Connection CRD must be merged"), +/// S3Connection::try_convert, +/// )]; /// -/// let conversion_webhook_options = ConversionWebhookOptions{ +/// let conversion_webhook_options = ConversionWebhookOptions { /// disable_crd_maintenance, /// field_manager: "my-field-manager".to_owned(), /// }; -/// let (conversion_webhook, initial_reconcile_rx) = ConversionWebhook::new( -/// crds_and_handlers, -/// client, -/// conversion_webhook_options, -/// ); +/// let (conversion_webhook, initial_reconcile_rx) = +/// ConversionWebhook::new(crds_and_handlers, client, conversion_webhook_options); /// /// let webhook_options = todo!(); -/// let webhook_server = WebhookServer::new( -/// vec![Box::new(conversion_webhook)], -/// webhook_options, -/// ).await.unwrap(); +/// let webhook_server = WebhookServer::new(vec![Box::new(conversion_webhook)], webhook_options) +/// .await +/// .unwrap(); /// webhook_server.run().await.unwrap(); /// # } /// ``` diff --git a/crates/stackable-webhook/src/webhooks/mutating_webhook.rs b/crates/stackable-webhook/src/webhooks/mutating_webhook.rs index 744e0a161..a214356c7 100644 --- a/crates/stackable-webhook/src/webhooks/mutating_webhook.rs +++ b/crates/stackable-webhook/src/webhooks/mutating_webhook.rs @@ -40,13 +40,17 @@ pub enum MutatingWebhookError { /// ``` /// use std::sync::Arc; /// -/// use k8s_openapi::api::admissionregistration::v1::MutatingWebhookConfiguration; -/// use k8s_openapi::api::apps::v1::StatefulSet; -/// -/// use stackable_operator::kube::Client; -/// use stackable_operator::kube::core::admission::{AdmissionRequest, AdmissionResponse}; -/// use stackable_webhook::WebhookServer; -/// use stackable_webhook::webhooks::{MutatingWebhook, MutatingWebhookOptions}; +/// use k8s_openapi::api::{ +/// admissionregistration::v1::MutatingWebhookConfiguration, apps::v1::StatefulSet, +/// }; +/// use stackable_operator::kube::{ +/// Client, +/// core::admission::{AdmissionRequest, AdmissionResponse}, +/// }; +/// use stackable_webhook::{ +/// WebhookServer, +/// webhooks::{MutatingWebhook, MutatingWebhookOptions}, +/// }; /// /// # async fn docs() { /// // The Kubernetes client @@ -56,7 +60,7 @@ pub enum MutatingWebhookError { /// // Read in from user input, e.g. CLI arguments /// let disable_mwc_maintenance = false; /// -/// let mutating_webhook_options = MutatingWebhookOptions{ +/// let mutating_webhook_options = MutatingWebhookOptions { /// disable_mwc_maintenance, /// field_manager: "my-field-manager".to_owned(), /// }; @@ -69,7 +73,9 @@ pub enum MutatingWebhookError { /// )); /// /// let webhook_options = todo!(); -/// let webhook_server = WebhookServer::new(vec![mutating_webhook], webhook_options).await.unwrap(); +/// let webhook_server = WebhookServer::new(vec![mutating_webhook], webhook_options) +/// .await +/// .unwrap(); /// webhook_server.run().await.unwrap(); /// # } /// @@ -77,11 +83,13 @@ pub enum MutatingWebhookError { /// let webhook_name = "pod-labeler.stackable.tech"; /// /// MutatingWebhookConfiguration { -/// webhooks: Some(vec![k8s_openapi::api::admissionregistration::v1::MutatingWebhook { -/// // This is checked by the stackable_webhook code -/// admission_review_versions: vec!["v1".to_owned()], -/// ..Default::default() -/// }]), +/// webhooks: Some(vec![ +/// k8s_openapi::api::admissionregistration::v1::MutatingWebhook { +/// // This is checked by the stackable_webhook code +/// admission_review_versions: vec!["v1".to_owned()], +/// ..Default::default() +/// }, +/// ]), /// ..Default::default() /// } /// } From 2ddca671d0db366478cf8422ac934f2b779bc442 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Mon, 22 Dec 2025 15:16:18 +0100 Subject: [PATCH 5/5] More merge conflicts --- crates/stackable-webhook/src/options.rs | 145 -------- .../src/servers/conversion.rs | 319 ------------------ 2 files changed, 464 deletions(-) delete mode 100644 crates/stackable-webhook/src/options.rs delete mode 100644 crates/stackable-webhook/src/servers/conversion.rs diff --git a/crates/stackable-webhook/src/options.rs b/crates/stackable-webhook/src/options.rs deleted file mode 100644 index ed713ebfc..000000000 --- a/crates/stackable-webhook/src/options.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! Contains available options to configure the [WebhookServer]. - -use std::{ - net::{IpAddr, SocketAddr}, - path::PathBuf, -}; - -use stackable_certs::PrivateKeyType; - -use crate::WebhookServer; - -/// Specifies available webhook server options. -/// -/// The [`Default`] implementation for this struct contains the following values: -/// -/// - The socket binds to 127.0.0.1 on port 8443 (HTTPS) -/// - An empty list of SANs is provided to the certificate the TLS server uses. -/// -/// ### Example with Custom HTTPS IP Address and Port -/// -/// ``` -/// use stackable_webhook::WebhookOptions; -/// -/// // Set IP address and port at the same time -/// let options = WebhookOptions::builder() -/// .bind_address([0, 0, 0, 0], 12345) -/// .build(); -/// -/// // Set IP address only -/// let options = WebhookOptions::builder().bind_ip([0, 0, 0, 0]).build(); -/// -/// // Set port only -/// let options = WebhookOptions::builder().bind_port(12345).build(); -/// ``` -#[derive(Debug)] -pub struct WebhookOptions { - /// The default HTTPS socket address the [`TcpListener`][tokio::net::TcpListener] - /// binds to. - pub socket_addr: SocketAddr, - - /// The subject alterative DNS names that should be added to the certificates generated for this - /// webhook. - pub subject_alterative_dns_names: Vec, -} - -impl Default for WebhookOptions { - fn default() -> Self { - Self::builder().build() - } -} - -impl WebhookOptions { - /// Returns the default [`WebhookOptionsBuilder`] which allows to selectively - /// customize the options. See the documentation for [`WebhookOptions`] for more - /// information on available functions. - pub fn builder() -> WebhookOptionsBuilder { - WebhookOptionsBuilder::default() - } -} - -/// The [`WebhookOptionsBuilder`] which allows to selectively customize the webhook -/// server [`WebhookOptions`]. -/// -/// Usually, this struct is not constructed manually, but instead by calling -/// [`WebhookOptions::builder()`] or [`WebhookOptionsBuilder::default()`]. -#[derive(Debug, Default)] -pub struct WebhookOptionsBuilder { - socket_addr: Option, - subject_alterative_dns_names: Vec, -} - -impl WebhookOptionsBuilder { - /// Sets the socket address the webhook server uses to bind for HTTPS. - pub fn bind_address(mut self, bind_ip: impl Into, bind_port: u16) -> Self { - self.socket_addr = Some(SocketAddr::new(bind_ip.into(), bind_port)); - self - } - - /// Sets the IP address of the socket address the webhook server uses to - /// bind for HTTPS. - pub fn bind_ip(mut self, bind_ip: impl Into) -> Self { - let addr = self - .socket_addr - .get_or_insert(WebhookServer::DEFAULT_SOCKET_ADDRESS); - addr.set_ip(bind_ip.into()); - self - } - - /// Sets the port of the socket address the webhook server uses to bind - /// for HTTPS. - pub fn bind_port(mut self, bind_port: u16) -> Self { - let addr = self - .socket_addr - .get_or_insert(WebhookServer::DEFAULT_SOCKET_ADDRESS); - addr.set_port(bind_port); - self - } - - /// Sets the subject alterative DNS names that should be added to the certificates generated for - /// this webhook. - pub fn subject_alterative_dns_names( - mut self, - subject_alterative_dns_name: Vec, - ) -> Self { - self.subject_alterative_dns_names = subject_alterative_dns_name; - self - } - - /// Adds the subject alterative DNS name to the list of names. - pub fn add_subject_alterative_dns_name( - mut self, - subject_alterative_dns_name: impl Into, - ) -> Self { - self.subject_alterative_dns_names - .push(subject_alterative_dns_name.into()); - self - } - - /// Builds the final [`WebhookOptions`] by using default values for any not - /// explicitly set option. - pub fn build(self) -> WebhookOptions { - WebhookOptions { - socket_addr: self - .socket_addr - .unwrap_or(WebhookServer::DEFAULT_SOCKET_ADDRESS), - subject_alterative_dns_names: self.subject_alterative_dns_names, - } - } -} - -#[derive(Debug)] -pub enum TlsOption { - AutoGenerate, - Mount { - private_key_type: PrivateKeyType, - private_key_path: PathBuf, - certificate_path: PathBuf, - }, -} - -impl Default for TlsOption { - fn default() -> Self { - Self::AutoGenerate - } -} diff --git a/crates/stackable-webhook/src/servers/conversion.rs b/crates/stackable-webhook/src/servers/conversion.rs deleted file mode 100644 index be1f51b92..000000000 --- a/crates/stackable-webhook/src/servers/conversion.rs +++ /dev/null @@ -1,319 +0,0 @@ -use std::{fmt::Debug, net::SocketAddr}; - -use axum::{Json, Router, routing::post}; -use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; -// Re-export this type because users of the conversion webhook server require -// this type to write the handler function. Instead of importing this type from -// kube directly, consumers can use this type instead. This also eliminates -// keeping the kube dependency version in sync between here and the operator. -pub use kube::core::conversion::ConversionReview; -use kube::{Client, ResourceExt}; -use snafu::{ResultExt, Snafu}; -use tokio::sync::{mpsc, oneshot}; -use tracing::instrument; -use x509_cert::Certificate; - -use crate::{ - WebhookError, WebhookHandler, WebhookServer, - maintainer::{CustomResourceDefinitionMaintainer, CustomResourceDefinitionMaintainerOptions}, - options::WebhookOptions, -}; - -#[derive(Debug, Snafu)] -pub enum ConversionWebhookError { - #[snafu(display("failed to create webhook server"))] - CreateWebhookServer { source: WebhookError }, - - #[snafu(display("failed to run webhook server"))] - RunWebhookServer { source: WebhookError }, - - #[snafu(display("failed to receive certificate from channel"))] - ReceiveCertificateFromChannel, - - #[snafu(display("failed to convert CA certificate into PEM format"))] - ConvertCaToPem { source: x509_cert::der::Error }, - - #[snafu(display("failed to reconcile CRDs"))] - ReconcileCrds { - #[snafu(source(from(ConversionWebhookError, Box::new)))] - source: Box, - }, - - #[snafu(display("failed to update CRD {crd_name:?}"))] - UpdateCrd { - source: kube::Error, - crd_name: String, - }, -} - -impl WebhookHandler for F -where - F: FnOnce(ConversionReview) -> ConversionReview, -{ - fn call(self, req: ConversionReview) -> ConversionReview { - self(req) - } -} - -// TODO: Add a builder, maybe with `bon`. -#[derive(Debug)] -pub struct ConversionWebhookOptions<'a> { - /// The bind address to bind the HTTPS server to. - pub socket_addr: SocketAddr, - - /// The namespace the operator/webhook is running in. - pub namespace: &'a str, - - /// The name of the Kubernetes service which points to the operator/webhook. - pub service_name: &'a str, -} - -/// A ready-to-use CRD conversion webhook server. -/// -/// See [`ConversionWebhookServer::new()`] for usage examples. -pub struct ConversionWebhookServer(WebhookServer); - -impl ConversionWebhookServer { - /// The default socket address the conversion webhook server binds to, see - /// [`WebhookServer::DEFAULT_SOCKET_ADDRESS`]. - pub const DEFAULT_SOCKET_ADDRESS: SocketAddr = WebhookServer::DEFAULT_SOCKET_ADDRESS; - - /// Creates and returns a new [`ConversionWebhookServer`], which expects POST requests being - /// made to the `/convert/{CRD_NAME}` endpoint. - /// - /// The TLS certificate is automatically generated and rotated. - /// - /// ## Parameters - /// - /// This function expects the following parameters: - /// - /// - `crds_and_handlers`: An iterator over a 2-tuple (pair) mapping a [`CustomResourceDefinition`] - /// to a handler function. In most cases, the generated `CustomResource::try_merge` function - /// should be used. It provides the expected `fn(ConversionReview) -> ConversionReview` - /// signature. - /// - `options`: Provides [`ConversionWebhookOptions`] to customize various parts of the - /// webhook server, eg. the socket address used to listen on. - /// - /// ## Return Values - /// - /// This function returns a [`Result`] which contains a 2-tuple (pair) of values for the [`Ok`] - /// variant: - /// - /// - The [`ConversionWebhookServer`] itself. This is used to run the server. See - /// [`ConversionWebhookServer::run`] for more details. - /// - The [`mpsc::Receiver`] which will be used to send out messages containing the newly - /// generated TLS certificate. This channel is used by the CRD maintainer to trigger a - /// reconcile of the CRDs it maintains. - /// - /// ## Example - /// - /// ```no_run - /// # use tokio_rustls::rustls::crypto::{CryptoProvider, ring::default_provider}; - /// use stackable_operator::crd::s3::{S3Connection, S3ConnectionVersion}; - /// use stackable_webhook::servers::{ConversionWebhookOptions, ConversionWebhookServer}; - /// - /// # #[tokio::main] - /// # async fn main() { - /// # CryptoProvider::install_default(default_provider()).unwrap(); - /// let crds_and_handlers = vec![( - /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) - /// .expect("the S3Connection CRD must be merged"), - /// S3Connection::try_convert, - /// )]; - /// - /// let options = ConversionWebhookOptions { - /// socket_addr: ConversionWebhookServer::DEFAULT_SOCKET_ADDRESS, - /// namespace: "stackable-operators", - /// service_name: "product-operator", - /// }; - /// - /// let (conversion_webhook_server, _certificate_rx) = - /// ConversionWebhookServer::new(crds_and_handlers, options) - /// .await - /// .unwrap(); - /// - /// conversion_webhook_server.run().await.unwrap(); - /// # } - /// ``` - #[instrument(name = "create_conversion_webhook_server", skip(crds_and_handlers))] - pub async fn new( - crds_and_handlers: impl IntoIterator, - options: ConversionWebhookOptions<'_>, - ) -> Result<(Self, mpsc::Receiver), ConversionWebhookError> - where - H: WebhookHandler + Clone + Send + Sync + 'static, - { - tracing::debug!("create new conversion webhook server"); - - let mut router = Router::new(); - - for (crd, handler) in crds_and_handlers { - let crd_name = crd.name_any(); - let handler_fn = |Json(review): Json| async { - let review = handler.call(review); - Json(review) - }; - - // TODO (@Techassi): Make this part of the trait mentioned above - let route = format!("/convert/{crd_name}"); - router = router.route(&route, post(handler_fn)); - } - - let ConversionWebhookOptions { - socket_addr, - namespace: operator_namespace, - service_name: operator_service_name, - } = &options; - - // This is how Kubernetes calls us, so it decides about the naming. - // AFAIK we can not influence this, so this is the only SAN entry needed. - // TODO (@Techassi): The cluster domain should be included here, so that (non Kubernetes) - // HTTP clients can use the FQDN of the service for testing or user use-cases. - let subject_alterative_dns_name = - format!("{operator_service_name}.{operator_namespace}.svc",); - - let webhook_options = WebhookOptions { - subject_alterative_dns_names: vec![subject_alterative_dns_name], - socket_addr: *socket_addr, - }; - - let (server, certificate_rx) = WebhookServer::new(router, webhook_options) - .await - .context(CreateWebhookServerSnafu)?; - - Ok((Self(server), certificate_rx)) - } - - /// Creates and returns a tuple consisting of a [`ConversionWebhookServer`], a [`CustomResourceDefinitionMaintainer`], - /// and a [`oneshot::Receiver`]. - /// - /// ## Parameters - /// - /// - `crds_and_handlers`: An iterator over a 2-tuple (pair) mapping a [`CustomResourceDefinition`] - /// to a handler function. In most cases, the generated `CustomResource::try_merge` function - /// should be used. It provides the expected `fn(ConversionReview) -> ConversionReview` - /// signature. - /// - `operator_service_name`: The name of the Kubernetes service name which points to the - /// operator/conversion webhook. This is used to construct the service reference in the CRD - /// `spec.conversion` field. - /// - `operator_namespace`: The namespace the operator runs in. This is used to construct the - /// service reference in the CRD `spec.conversion` field. - /// - `disable_maintainer`: A boolean value to indicate if the [`CustomResourceDefinitionMaintainer`] - /// should be disabled. - /// - `client`: A [`kube::Client`] used to maintain the custom resource definitions. - /// - /// See the referenced items for more details on usage. - /// - /// ## Return Values - /// - /// - The [`ConversionWebhookServer`] itself. This is used to run the server. See - /// [`ConversionWebhookServer::run`] for more details. - /// - The [`CustomResourceDefinitionMaintainer`] which is used to run the maintainer. See - /// [`CustomResourceDefinitionMaintainer::run`] for more details. - /// - A [`oneshot::Receiver`] which is triggered after the initial reconciliation of the CRDs - /// succeeded. This signal can be used to deploy any custom resources defined by these CRDs. - /// - /// ## Example - /// - /// ```no_run - /// # use futures_util::TryFutureExt; - /// # use tokio_rustls::rustls::crypto::{CryptoProvider, ring::default_provider}; - /// use stackable_operator::{ - /// crd::s3::{S3Connection, S3ConnectionVersion}, - /// kube::Client, - /// }; - /// use stackable_webhook::servers::{ConversionWebhookOptions, ConversionWebhookServer}; - /// - /// # #[tokio::main] - /// # async fn main() { - /// # CryptoProvider::install_default(default_provider()).unwrap(); - /// let client = Client::try_default().await.unwrap(); - /// - /// let crds_and_handlers = vec![( - /// S3Connection::merged_crd(S3ConnectionVersion::V1Alpha1) - /// .expect("the S3Connection CRD must be merged"), - /// S3Connection::try_convert, - /// )]; - /// - /// let (conversion_webhook_server, crd_maintainer, _initial_reconcile_rx) = - /// ConversionWebhookServer::with_maintainer( - /// crds_and_handlers, - /// "my-operator", - /// "my-namespace", - /// "my-field-manager", - /// false, - /// client, - /// ) - /// .await - /// .unwrap(); - /// - /// let conversion_webhook_server = conversion_webhook_server - /// .run() - /// .map_err(|err| err.to_string()); - /// - /// let crd_maintainer = crd_maintainer.run().map_err(|err| err.to_string()); - /// - /// // Run both the conversion webhook server and crd_maintainer concurrently, eg. with - /// // futures::try_join!. - /// futures_util::try_join!(conversion_webhook_server, crd_maintainer).unwrap(); - /// # } - /// ``` - pub async fn with_maintainer<'a, H>( - // TODO (@Techassi): Use a trait type here which can be used to build all part of the - // conversion webhook server and a CRD maintainer. - crds_and_handlers: impl IntoIterator + Clone, - operator_service_name: &'a str, - operator_namespace: &'a str, - field_manager: &'a str, - disable_maintainer: bool, - client: Client, - ) -> Result< - ( - Self, - CustomResourceDefinitionMaintainer<'a>, - oneshot::Receiver<()>, - ), - ConversionWebhookError, - > - where - H: WebhookHandler + Clone + Send + Sync + 'static, - { - let socket_addr = ConversionWebhookServer::DEFAULT_SOCKET_ADDRESS; - - // TODO (@Techassi): These should be moved into a builder - let webhook_options = ConversionWebhookOptions { - service_name: operator_service_name, - namespace: operator_namespace, - socket_addr, - }; - - let (conversion_webhook_server, certificate_rx) = - Self::new(crds_and_handlers.clone(), webhook_options).await?; - - let definitions = crds_and_handlers.into_iter().map(|(crd, _)| crd); - - // TODO (@Techassi): These should be moved into a builder - let maintainer_options = CustomResourceDefinitionMaintainerOptions { - webhook_https_port: socket_addr.port(), - disabled: disable_maintainer, - operator_service_name, - operator_namespace, - field_manager, - }; - - let (maintainer, initial_reconcile_rx) = CustomResourceDefinitionMaintainer::new( - client, - certificate_rx, - definitions, - maintainer_options, - ); - - Ok((conversion_webhook_server, maintainer, initial_reconcile_rx)) - } - - /// Runs the [`ConversionWebhookServer`] asynchronously. - pub async fn run(self) -> Result<(), ConversionWebhookError> { - tracing::info!("run conversion webhook server"); - self.0.run().await.context(RunWebhookServerSnafu) - } -}