From ab81bddb91f7fce08ff5638744f2b6b8789b99bb Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 15 Feb 2026 13:26:21 +0100 Subject: [PATCH] add type annotations to opaque CRD schema fields (#7952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: add type annotations to opaque CRD schema fields The WindmillInstance CRD failed K8s structural schema validation because four `Option` fields (`object_store_cache_config`, `secret_backend`, `slack`, `teams`) generated schemas without a `type` property. Kubernetes requires all specified object fields to declare a type. Add a `schemars(schema_with)` annotation that emits `{"type": "object", "nullable": true, "x-kubernetes-preserve-unknown-fields": true}` for these opaque settings fields. Co-Authored-By: Claude Opus 4.6 * feat: enable operator in CE builds The operator feature was only included in `ee_core`, meaning CE images couldn't run `windmill operator` as a proper k8s controller — the subcommand silently fell through to standalone mode. Add `operator` to `ce_core` so CE builds include the k8s controller that watches and reconciles WindmillInstance CRDs. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- backend/Cargo.toml | 2 +- .../windmill-common/src/instance_config.rs | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 61e9f722f1..6987ce617f 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -166,7 +166,7 @@ oss_core = [ "static_frontend", "mcp", "bedrock", "inline_preview", "quickjs" ] -ce_core = ["oss_core", "private"] +ce_core = ["oss_core", "private", "operator"] ee_core = [ "enterprise", "stripe", "prometheus", "cloud", "kafka", "sqs_trigger", "nats", "gcp_trigger", diff --git a/backend/windmill-common/src/instance_config.rs b/backend/windmill-common/src/instance_config.rs index f8bdb08578..609d76b138 100644 --- a/backend/windmill-common/src/instance_config.rs +++ b/backend/windmill-common/src/instance_config.rs @@ -164,6 +164,31 @@ pub struct InstanceConfig { // Global settings // --------------------------------------------------------------------------- +/// Generate a schema for opaque JSON objects (used for EE-private settings). +/// Produces `{"type": "object", "nullable": true}` so the CRD passes K8s +/// structural schema validation while still accepting any JSON object. +#[cfg(feature = "instance_config_schema")] +fn opaque_json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + schemars::schema::SchemaObject { + instance_type: Some(schemars::schema::InstanceType::Object.into()), + metadata: Some(Box::default()), + extensions: { + let mut m = schemars::Map::new(); + m.insert( + "nullable".to_string(), + serde_json::Value::Bool(true), + ); + m.insert( + "x-kubernetes-preserve-unknown-fields".to_string(), + serde_json::Value::Bool(true), + ); + m + }, + ..Default::default() + } + .into() +} + /// Typed global settings with schema validation. /// Known settings have explicit fields; unknown settings pass through via `extra`. #[derive(Deserialize, Serialize, Clone, Debug, Default)] @@ -265,6 +290,7 @@ pub struct GlobalSettings { #[serde(skip_serializing_if = "Option::is_none")] pub otel_tracing_proxy: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "instance_config_schema", schemars(schema_with = "opaque_json_schema"))] pub object_store_cache_config: Option, #[serde(skip_serializing_if = "Option::is_none")] pub critical_error_channels: Option>, @@ -277,10 +303,13 @@ pub struct GlobalSettings { // Opaque settings (EE-private structs or no clear schema) #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "instance_config_schema", schemars(schema_with = "opaque_json_schema"))] pub secret_backend: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "instance_config_schema", schemars(schema_with = "opaque_json_schema"))] pub slack: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "instance_config_schema", schemars(schema_with = "opaque_json_schema"))] pub teams: Option, /// Catch-all for settings not yet covered by typed fields.