diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index 5382a1f7c..15f0b4cfb 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- BREAKING: Add a new CLI flag/env to disabling CRD maintenance: `--disable-crd-maintenance` ([#1085]). + +[#1085]: https://github.com/stackabletech/operator-rs/pull/1085 + ## [0.96.0] - 2025-08-25 ### Added diff --git a/crates/stackable-operator/src/cli.rs b/crates/stackable-operator/src/cli.rs index 3c91bae9d..d3d244edb 100644 --- a/crates/stackable-operator/src/cli.rs +++ b/crates/stackable-operator/src/cli.rs @@ -208,6 +208,7 @@ pub enum Command { /// operator_namespace: "stackable-operators".to_string(), /// operator_service_name: "foo-operator".to_string(), /// }, +/// disable_crd_maintenance: false, /// }, /// })); /// ``` @@ -245,6 +246,17 @@ pub struct ProductOperatorRun { /// Provides a specific namespace to watch (instead of watching all namespaces) #[arg(long, env, default_value = "")] pub watch_namespace: WatchNamespace, + + /// Don't maintain the CustomResourceDefinitions (CRDs) the operator is responsible for. + /// + /// Maintenance includes creating the CRD initially, adding new versions and keeping the TLS + /// certificate of webhooks up to date. Turning this off can be desirable to reduce the RBAC + /// permissions of the operator. + /// + /// WARNING: If you disable CRD maintenance you are responsible for maintaining it, including, + /// but not limited to, the points above. + #[arg(long, env)] + pub disable_crd_maintenance: bool, } /// All the CLI arguments that all (or at least most) Stackable applications use. diff --git a/crates/stackable-webhook/CHANGELOG.md b/crates/stackable-webhook/CHANGELOG.md index c0571e4a7..5615c7219 100644 --- a/crates/stackable-webhook/CHANGELOG.md +++ b/crates/stackable-webhook/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- BREAKING: Support disabling CRD maintenance using a new boolean flag in `ConversionWebhookOptions` ([#1085]). + +[#1085]: https://github.com/stackabletech/operator-rs/pull/1085 + ## [0.5.0] - 2025-08-21 ### Changed diff --git a/crates/stackable-webhook/src/servers/conversion.rs b/crates/stackable-webhook/src/servers/conversion.rs index ed6b63579..baa79c241 100644 --- a/crates/stackable-webhook/src/servers/conversion.rs +++ b/crates/stackable-webhook/src/servers/conversion.rs @@ -78,6 +78,12 @@ pub struct ConversionWebhookOptions { /// The name of the Kubernetes service which points to the operator/webhook. pub service_name: String, + /// If the CRDs should be maintained automatically. Use the (negated) value from + /// `stackable_operator::cli::ProductOperatorRun::disable_crd_maintenance` + /// for this. + // # Because of https://github.com/rust-lang/cargo/issues/3475 we can not use a real link here + pub maintain_crds: bool, + /// The field manager used to apply Kubernetes objects, typically the operator name, e.g. /// `airflow-operator`. pub field_manager: String, @@ -97,11 +103,12 @@ impl ConversionWebhookServer { /// Creates a new conversion webhook server, which expects POST requests being made to the /// `/convert/{crd name}` endpoint. /// - /// You need to provide two things for every CRD passed in via the `crds_and_handlers` argument: + /// You need to provide a few things for every CRD passed in via the `crds_and_handlers` argument: /// /// 1. The CRD /// 2. A conversion function to convert between CRD versions. Typically you would use the - /// the auto-generated `try_convert` function on CRD spec definition structs for this. + /// the auto-generated `try_convert` function on CRD spec definition structs for this. + /// 3. A [`kube::Client`] used to create/update the CRDs. /// /// The [`ConversionWebhookServer`] takes care of reconciling the CRDs into the Kubernetes /// cluster and takes care of adding itself as conversion webhook. This includes TLS @@ -119,14 +126,18 @@ impl ConversionWebhookServer { /// use stackable_operator::{ /// kube::Client, /// crd::s3::{S3Connection, S3ConnectionVersion}, - /// cli::OperatorEnvironmentOptions, + /// cli::ProductOperatorRun, /// }; /// /// # async fn test() { /// // Things that should already be in you operator: /// const OPERATOR_NAME: &str = "product-operator"; /// let client = Client::try_default().await.expect("failed to create Kubernetes client"); - /// let operator_environment = OperatorEnvironmentOptions::parse(); + /// let ProductOperatorRun { + /// operator_environment, + /// disable_crd_maintenance, + /// .. + /// } = ProductOperatorRun::parse(); /// /// let crds_and_handlers = [ /// ( @@ -140,9 +151,10 @@ impl ConversionWebhookServer { /// socket_addr: format!("0.0.0.0:{CONVERSION_WEBHOOK_HTTPS_PORT}") /// .parse() /// .expect("static address is always valid"), - /// field_manager: OPERATOR_NAME.to_owned(), /// namespace: operator_environment.operator_namespace, /// service_name: operator_environment.operator_service_name, + /// maintain_crds: !disable_crd_maintenance, + /// field_manager: OPERATOR_NAME.to_owned(), /// }; /// /// // Construct the conversion webhook server @@ -205,9 +217,10 @@ impl ConversionWebhookServer { let ConversionWebhookOptions { socket_addr, - field_manager, namespace: operator_namespace, service_name: operator_service_name, + maintain_crds, + field_manager, } = &options; // This is how Kubernetes calls us, so it decides about the naming. @@ -233,28 +246,33 @@ impl ConversionWebhookServer { .recv() .await .context(ReceiveCertificateFromChannelSnafu)?; - Self::reconcile_crds( - &client, - field_manager, - &crds, - operator_namespace, - operator_service_name, - current_cert, - ) - .await - .context(ReconcileCrdsSnafu)?; - - try_join!( - Self::run_webhook_server(server), - Self::run_crd_reconciliation_loop( - cert_rx, + + if *maintain_crds { + Self::reconcile_crds( &client, field_manager, &crds, operator_namespace, operator_service_name, - ), - )?; + current_cert, + ) + .await + .context(ReconcileCrdsSnafu)?; + + try_join!( + Self::run_webhook_server(server), + Self::run_crd_reconciliation_loop( + cert_rx, + &client, + field_manager, + &crds, + operator_namespace, + operator_service_name, + ), + )?; + } else { + Self::run_webhook_server(server).await?; + }; Ok(()) }