From 73361fd3774765c2bac1844961605cd7be51e65c Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 20 Jul 2026 12:35:54 +0200 Subject: [PATCH 1/8] add rbac labels, use infallible functions --- Cargo.lock | 11 +-- Cargo.toml | 2 +- .../operator-binary/src/airflow_controller.rs | 62 ++++-------- .../src/controller/build/mod.rs | 99 ++++++++++++++----- .../src/controller/build/resource/executor.rs | 8 +- .../src/controller/build/resource/listener.rs | 17 +++- .../src/controller/build/resource/mod.rs | 1 + .../src/controller/build/resource/pdb.rs | 2 +- .../src/controller/build/resource/rbac.rs | 43 ++++++++ .../src/controller/build/resource/service.rs | 6 +- .../controller/build/resource/statefulset.rs | 12 ++- rust/operator-binary/src/controller/mod.rs | 44 +++++++-- rust/operator-binary/src/crd/mod.rs | 9 -- 13 files changed, 207 insertions(+), 109 deletions(-) create mode 100644 rust/operator-binary/src/controller/build/resource/rbac.rs diff --git a/Cargo.lock b/Cargo.lock index 1bd887e9..9d2fd279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1607,7 +1607,6 @@ dependencies = [ [[package]] name = "k8s-version" version = "0.1.3" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "darling", "regex", @@ -3038,7 +3037,6 @@ dependencies = [ [[package]] name = "stackable-certs" version = "0.4.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "const-oid", "ecdsa", @@ -3061,8 +3059,7 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.113.3" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" +version = "0.113.4" dependencies = [ "base64", "clap", @@ -3107,7 +3104,6 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.3.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "darling", "proc-macro2", @@ -3118,7 +3114,6 @@ dependencies = [ [[package]] name = "stackable-shared" version = "0.1.2" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "jiff", "k8s-openapi", @@ -3135,7 +3130,6 @@ dependencies = [ [[package]] name = "stackable-telemetry" version = "0.6.5" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "axum", "clap", @@ -3159,7 +3153,6 @@ dependencies = [ [[package]] name = "stackable-versioned" version = "0.11.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "kube", "schemars", @@ -3173,7 +3166,6 @@ dependencies = [ [[package]] name = "stackable-versioned-macros" version = "0.11.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "convert_case", "convert_case_extras", @@ -3191,7 +3183,6 @@ dependencies = [ [[package]] name = "stackable-webhook" version = "0.9.2" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#013bbf43f7006a4ddfc08a147f68441ed88b462b" dependencies = [ "arc-swap", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 00b4eabe..f7e9583f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ tracing = "0.1" [patch."https://github.com/stackabletech/operator-rs.git"] # stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" } -# stackable-operator = { path = "../operator-rs/crates/stackable-operator" } +stackable-operator = { path = "../operator-rs/crates/stackable-operator" } diff --git a/rust/operator-binary/src/airflow_controller.rs b/rust/operator-binary/src/airflow_controller.rs index 7d2bbbd3..615e18fe 100644 --- a/rust/operator-binary/src/airflow_controller.rs +++ b/rust/operator-binary/src/airflow_controller.rs @@ -9,14 +9,12 @@ use snafu::{ResultExt, Snafu}; use stackable_operator::{ cli::OperatorEnvironmentOptions, cluster_resources::ClusterResourceApplyStrategy, - commons::{random_secret_creation, rbac::build_rbac_resources}, + commons::random_secret_creation, k8s_openapi::api::core::v1::EnvVar, kube::{ - ResourceExt, core::{DeserializeGuard, error_boundary}, runtime::controller::Action, }, - kvp::LabelError, logging::controller::ReconcilerError, shared::time::Duration, status::condition::{ @@ -30,7 +28,7 @@ use strum::{EnumDiscriminants, IntoStaticStr}; use crate::{ controller::{ValidatedCluster, build, controller_name, operator_name, product_name}, crd::{ - APP_NAME, AirflowClusterStatus, OPERATOR_NAME, + AirflowClusterStatus, OPERATOR_NAME, internal_secret::{ FERNET_KEY_SECRET_KEY, INTERNAL_SECRET_SECRET_KEY, JWT_SECRET_SECRET_KEY, }, @@ -58,21 +56,6 @@ pub enum Error { source: stackable_operator::cluster_resources::Error, }, - #[snafu(display("failed to patch service account"))] - ApplyServiceAccount { - source: stackable_operator::cluster_resources::Error, - }, - - #[snafu(display("failed to patch role binding: {source}"))] - ApplyRoleBinding { - source: stackable_operator::cluster_resources::Error, - }, - - #[snafu(display("failed to build RBAC objects"))] - BuildRBACObjects { - source: stackable_operator::commons::rbac::Error, - }, - #[snafu(display("failed to build the Kubernetes resources"))] BuildResources { source: build::Error }, @@ -101,9 +84,6 @@ pub enum Error { source: crate::controller::validate::Error, }, - #[snafu(display("failed to build label"))] - BuildLabel { source: LabelError }, - #[snafu(display("AirflowCluster object is invalid"))] InvalidAirflowCluster { source: error_boundary::InvalidObject, @@ -159,33 +139,25 @@ pub async fn reconcile_airflow( &airflow.spec.object_overrides, ); - let required_labels = cluster_resources - .get_required_labels() - .context(BuildLabelSnafu)?; - - let (rbac_sa, rbac_rolebinding) = - build_rbac_resources(airflow, APP_NAME, required_labels).context(BuildRBACObjectsSnafu)?; - - // The ServiceAccount name is deterministic on the built object, so the build step does not - // depend on the applied ServiceAccount. - let service_account_name = rbac_sa.name_any(); - - cluster_resources - .add(client, rbac_sa) - .await - .context(ApplyServiceAccountSnafu)?; - cluster_resources - .add(client, rbac_rolebinding) - .await - .context(ApplyRoleBindingSnafu)?; - - let resources = - build::build(&validated_cluster, &service_account_name).context(BuildResourcesSnafu)?; + let resources = build::build(&validated_cluster).context(BuildResourcesSnafu)?; let mut ss_cond_builder = StatefulSetConditionBuilder::default(); // Apply order is: StatefulSets last (a changed mounted ConfigMap/Secret - // must exist first, else Pods restart -- commons-operator#111). + // must exist first, else Pods restart -- commons-operator#111). The ServiceAccount comes + // first because the Pods reference it at creation time. + for service_account in resources.service_accounts { + cluster_resources + .add(client, service_account) + .await + .context(ApplyResourceSnafu)?; + } + for role_binding in resources.role_bindings { + cluster_resources + .add(client, role_binding) + .await + .context(ApplyResourceSnafu)?; + } for service in resources.services { cluster_resources .add(client, service) diff --git a/rust/operator-binary/src/controller/build/mod.rs b/rust/operator-binary/src/controller/build/mod.rs index f148f167..65d51626 100644 --- a/rust/operator-binary/src/controller/build/mod.rs +++ b/rust/operator-binary/src/controller/build/mod.rs @@ -11,6 +11,7 @@ use crate::{ executor::build_executor_template_config_map, listener::build_group_listener, pdb::build_pdb, + rbac::{build_role_binding, build_service_account}, service::{build_rolegroup_headless_service, build_rolegroup_metrics_service}, statefulset::build_server_rolegroup_statefulset, }, @@ -47,14 +48,7 @@ pub enum Error { /// Does not need a Kubernetes client: every reference to another Kubernetes resource is already /// dereferenced and validated by this point. Cluster configuration is likewise already validated, /// so the errors returned here are resource-assembly failures only. -/// -/// `service_account_name` is the name of the RBAC `ServiceAccount` the role-group Pods and the -/// Kubernetes-executor pod template run under (RBAC resources are built and applied separately, -/// in the reconcile step). -pub fn build( - cluster: &ValidatedCluster, - service_account_name: &str, -) -> Result { +pub fn build(cluster: &ValidatedCluster) -> Result { let mut stateful_sets = vec![]; let mut services = vec![]; let mut listeners = vec![]; @@ -81,7 +75,6 @@ pub fn build( let executor_template_config_map = build_executor_template_config_map( cluster, - service_account_name, &executor_template.config, &executor_template.env_overrides, &executor_template.pod_overrides, @@ -123,7 +116,7 @@ pub fn build( config_maps.push( config_map::build_rolegroup_config_map( cluster, - &role.role_name(), + &ValidatedCluster::role_name(role), role_group_name, &rg_config.config_overrides, logging, @@ -140,7 +133,6 @@ pub fn build( role_group_name, rg_config, logging, - service_account_name, ) .context(StatefulSetSnafu { role_group: role_group_name.clone(), @@ -155,11 +147,15 @@ pub fn build( listeners, config_maps, pod_disruption_budgets, + service_accounts: vec![build_service_account(cluster)], + role_bindings: vec![build_role_binding(cluster)], }) } #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use stackable_operator::kube::Resource; use super::build; @@ -184,7 +180,7 @@ mod tests { apiVersion: airflow.stackable.tech/v1alpha2 kind: AirflowCluster metadata: - name: airflow + name: my-airflow namespace: default uid: e6ac237d-a6d4-43a1-8135-f36506110912 spec: @@ -263,25 +259,84 @@ mod tests { #[test] fn build_produces_expected_resource_names() { let cluster = celery_executor_cluster(); - let resources = build(&cluster, "airflow-serviceaccount").expect("build succeeds"); + let resources = build(&cluster).expect("build succeeds"); assert_eq!( sorted_names(&resources.stateful_sets), - ["airflow-scheduler-default", "airflow-webserver-default"] + [ + "my-airflow-scheduler-default", + "my-airflow-webserver-default" + ] ); // One headless and one metrics Service per role group. assert_eq!(resources.services.len(), 4); assert_eq!( sorted_names(&resources.config_maps), - ["airflow-scheduler-default", "airflow-webserver-default"] + [ + "my-airflow-scheduler-default", + "my-airflow-webserver-default" + ] ); // The webserver is the only role with a group Listener. - assert_eq!(sorted_names(&resources.listeners), ["airflow-webserver"]); + assert_eq!(sorted_names(&resources.listeners), ["my-airflow-webserver"]); // A default PDB per role (the Celery worker included). assert_eq!( sorted_names(&resources.pod_disruption_budgets), - ["airflow-scheduler", "airflow-webserver", "airflow-worker"] + [ + "my-airflow-scheduler", + "my-airflow-webserver", + "my-airflow-worker" + ] + ); + } + + /// Locks the RBAC resource names, the roleRef, and the recommended label set against + /// accidental drift. The fixture's cluster name deliberately differs from the product name so + /// that swapped `name`/`instance` label values cannot pass unnoticed. + #[test] + fn build_produces_rbac() { + let cluster = celery_executor_cluster(); + let resources = build(&cluster).expect("build succeeds"); + + assert_eq!( + sorted_names(&resources.service_accounts), + ["my-airflow-serviceaccount"] + ); + assert_eq!( + sorted_names(&resources.role_bindings), + ["my-airflow-rolebinding"] + ); + + let expected_labels = BTreeMap::from( + [ + ("app.kubernetes.io/component", "none"), + ("app.kubernetes.io/instance", "my-airflow"), + ( + "app.kubernetes.io/managed-by", + "airflow.stackable.tech_airflowcluster", + ), + ("app.kubernetes.io/name", "airflow"), + ("app.kubernetes.io/role-group", "none"), + ("app.kubernetes.io/version", "3.1.6-stackable0.0.0-dev"), + ("stackable.tech/vendor", "Stackable"), + ] + .map(|(key, value)| (key.to_string(), value.to_string())), ); + let service_account = resources + .service_accounts + .first() + .expect("a ServiceAccount is built"); + assert_eq!( + service_account.metadata.labels, + Some(expected_labels.clone()) + ); + + let role_binding = resources + .role_bindings + .first() + .expect("a RoleBinding is built"); + assert_eq!(role_binding.metadata.labels, Some(expected_labels)); + assert_eq!(role_binding.role_ref.name, "airflow-clusterrole"); } /// The Kubernetes-executor branch of `build()` (moved here from `reconcile`) additionally emits @@ -290,15 +345,15 @@ mod tests { #[test] fn build_kubernetes_executor_adds_pod_template_config_maps() { let cluster = kubernetes_executor_cluster(); - let resources = build(&cluster, "airflow-serviceaccount").expect("build succeeds"); + let resources = build(&cluster).expect("build succeeds"); assert_eq!( sorted_names(&resources.config_maps), [ - "airflow-executor-kubernetes", - "airflow-executor-pod-template", - "airflow-scheduler-default", - "airflow-webserver-default", + "my-airflow-executor-kubernetes", + "my-airflow-executor-pod-template", + "my-airflow-scheduler-default", + "my-airflow-webserver-default", ] ); } diff --git a/rust/operator-binary/src/controller/build/resource/executor.rs b/rust/operator-binary/src/controller/build/resource/executor.rs index b4b6830d..91eaa03f 100644 --- a/rust/operator-binary/src/controller/build/resource/executor.rs +++ b/rust/operator-binary/src/controller/build/resource/executor.rs @@ -76,7 +76,6 @@ type Result = std::result::Result; pub fn build_executor_template_config_map( cluster: &ValidatedCluster, - sa_name: &str, executor_config: &ValidatedAirflowConfig, env_overrides: &HashMap, pod_overrides: &PodTemplateSpec, @@ -99,7 +98,12 @@ pub fn build_executor_template_config_map( pb.metadata(pb_metadata) .image_pull_secrets_from_product_image(resolved_product_image) .affinity(&executor_config.affinity) - .service_account_name(sa_name) + .service_account_name( + cluster + .rbac_resource_names() + .service_account_name() + .to_string(), + ) .restart_policy("Never") .security_context(PodSecurityContextBuilder::new().fs_group(1000).build()); diff --git a/rust/operator-binary/src/controller/build/resource/listener.rs b/rust/operator-binary/src/controller/build/resource/listener.rs index 2083f259..5d56eb32 100644 --- a/rust/operator-binary/src/controller/build/resource/listener.rs +++ b/rust/operator-binary/src/controller/build/resource/listener.rs @@ -1,6 +1,11 @@ +use std::str::FromStr; + use stackable_operator::{ crd::listener, - v2::types::kubernetes::{ListenerClassName, ListenerName}, + v2::types::{ + kubernetes::{ListenerClassName, ListenerName}, + operator::RoleGroupName, + }, }; use crate::{ @@ -8,6 +13,10 @@ use crate::{ crd::{AirflowRole, HTTP_PORT, HTTP_PORT_NAME}, }; +// The group listener is a role-level object, so a constant `none` role-group is used as the +// role-group label value. +stackable_operator::constant!(PLACEHOLDER_LISTENER_ROLE_GROUP: RoleGroupName = "none"); + pub fn build_group_listener( cluster: &ValidatedCluster, role: &AirflowRole, @@ -18,11 +27,9 @@ pub fn build_group_listener( metadata: cluster .object_meta( listener_group_name, - // The group listener is a role-level object, so a constant `none` role-group is - // used as the role-group label value. cluster.recommended_labels_for( - &role.role_name(), - &"none".parse().expect("'none' is a valid role group name"), + &ValidatedCluster::role_name(role), + &PLACEHOLDER_LISTENER_ROLE_GROUP, ), ) .build(), diff --git a/rust/operator-binary/src/controller/build/resource/mod.rs b/rust/operator-binary/src/controller/build/resource/mod.rs index dd7798af..8f65cc3f 100644 --- a/rust/operator-binary/src/controller/build/resource/mod.rs +++ b/rust/operator-binary/src/controller/build/resource/mod.rs @@ -5,5 +5,6 @@ pub mod executor; pub mod listener; pub mod pdb; pub mod pod; +pub mod rbac; pub mod service; pub mod statefulset; diff --git a/rust/operator-binary/src/controller/build/resource/pdb.rs b/rust/operator-binary/src/controller/build/resource/pdb.rs index a7f5b338..22d82556 100644 --- a/rust/operator-binary/src/controller/build/resource/pdb.rs +++ b/rust/operator-binary/src/controller/build/resource/pdb.rs @@ -38,7 +38,7 @@ pub fn build_pdb( let pdb = pod_disruption_budget_builder_with_role( cluster, &product_name(), - &role.role_name(), + &ValidatedCluster::role_name(role), &operator_name(), &controller_name(), ) diff --git a/rust/operator-binary/src/controller/build/resource/rbac.rs b/rust/operator-binary/src/controller/build/resource/rbac.rs new file mode 100644 index 00000000..a60485bd --- /dev/null +++ b/rust/operator-binary/src/controller/build/resource/rbac.rs @@ -0,0 +1,43 @@ +//! Builds the RBAC resources (ServiceAccount + RoleBinding) shared by all role groups. + +use std::str::FromStr; + +use stackable_operator::{ + k8s_openapi::api::{core::v1::ServiceAccount, rbac::v1::RoleBinding}, + kvp::Labels, + v2::{ + rbac, + types::operator::{RoleGroupName, RoleName}, + }, +}; + +use crate::controller::ValidatedCluster; + +stackable_operator::constant!(NONE_ROLE_NAME: RoleName = "none"); +stackable_operator::constant!(NONE_ROLE_GROUP_NAME: RoleGroupName = "none"); + +/// Builds the [`ServiceAccount`] that the role-group Pods and the Kubernetes-executor Pods run +/// under. +pub fn build_service_account(cluster: &ValidatedCluster) -> ServiceAccount { + rbac::build_service_account( + cluster, + &cluster.rbac_resource_names(), + rbac_labels(cluster), + ) +} + +/// Builds the [`RoleBinding`] that binds the [`ServiceAccount`] from [`build_service_account`] to +/// the operator-deployed ClusterRole. +pub fn build_role_binding(cluster: &ValidatedCluster) -> RoleBinding { + rbac::build_role_binding( + cluster, + &cluster.rbac_resource_names(), + rbac_labels(cluster), + ) +} + +/// Both resources are shared by the whole cluster rather than tied to a role or role group, so +/// the recommended labels carry `none` for both values. +fn rbac_labels(cluster: &ValidatedCluster) -> Labels { + cluster.recommended_labels_for(&NONE_ROLE_NAME, &NONE_ROLE_GROUP_NAME) +} diff --git a/rust/operator-binary/src/controller/build/resource/service.rs b/rust/operator-binary/src/controller/build/resource/service.rs index d1481845..66c273c6 100644 --- a/rust/operator-binary/src/controller/build/resource/service.rs +++ b/rust/operator-binary/src/controller/build/resource/service.rs @@ -22,7 +22,7 @@ pub fn build_rolegroup_headless_service( metadata: cluster .object_meta( cluster - .resource_names(&role.role_name(), role_group_name) + .resource_names(&ValidatedCluster::role_name(role), role_group_name) .headless_service_name() .to_string(), cluster.recommended_labels(role, role_group_name), @@ -51,7 +51,7 @@ pub fn build_rolegroup_metrics_service( metadata: cluster .object_meta( cluster - .resource_names(&role.role_name(), role_group_name) + .resource_names(&ValidatedCluster::role_name(role), role_group_name) .metrics_service_name() .to_string(), cluster.recommended_labels(role, role_group_name), @@ -85,7 +85,7 @@ pub fn stateful_set_service_name( ) -> Option { Some( cluster - .resource_names(&role.role_name(), role_group_name) + .resource_names(&ValidatedCluster::role_name(role), role_group_name) .headless_service_name() .to_string(), ) diff --git a/rust/operator-binary/src/controller/build/resource/statefulset.rs b/rust/operator-binary/src/controller/build/resource/statefulset.rs index b357b93a..8a8b1065 100644 --- a/rust/operator-binary/src/controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/controller/build/resource/statefulset.rs @@ -103,7 +103,6 @@ pub fn build_server_rolegroup_statefulset( role_group_name: &RoleGroupName, validated_rg_config: &AirflowRoleGroupConfig, logging: &ValidatedLogging, - service_account_name: &str, ) -> Result { let merged_airflow_config = &validated_rg_config.config; let env_overrides = &validated_rg_config.env_overrides; @@ -116,8 +115,8 @@ pub fn build_server_rolegroup_statefulset( let executor = &validated_cluster.cluster_config.executor; let mut pb = PodBuilder::new(); - let resource_names = - validated_cluster.resource_names(&airflow_role.role_name(), role_group_name); + let resource_names = validated_cluster + .resource_names(&ValidatedCluster::role_name(airflow_role), role_group_name); let recommended_object_labels = validated_cluster.recommended_labels(airflow_role, role_group_name); @@ -140,7 +139,12 @@ pub fn build_server_rolegroup_statefulset( pb.metadata(pb_metadata) .image_pull_secrets_from_product_image(resolved_product_image) .affinity(&merged_airflow_config.affinity) - .service_account_name(service_account_name) + .service_account_name( + validated_cluster + .rbac_resource_names() + .service_account_name() + .to_string(), + ) .security_context(PodSecurityContextBuilder::new().fs_group(1000).build()); let mut airflow_container = new_container_builder(&Container::Airflow.to_container_name()); diff --git a/rust/operator-binary/src/controller/mod.rs b/rust/operator-binary/src/controller/mod.rs index d59e7b39..5869173e 100644 --- a/rust/operator-binary/src/controller/mod.rs +++ b/rust/operator-binary/src/controller/mod.rs @@ -20,8 +20,9 @@ use stackable_operator::{ }, k8s_openapi::api::{ apps::v1::StatefulSet, - core::v1::{ConfigMap, PodTemplateSpec, Service, Volume, VolumeMount}, + core::v1::{ConfigMap, PodTemplateSpec, Service, ServiceAccount, Volume, VolumeMount}, policy::v1::PodDisruptionBudget, + rbac::v1::RoleBinding, }, kube::{Resource, ResourceExt, api::ObjectMeta}, kvp::Labels, @@ -33,6 +34,7 @@ use stackable_operator::{ kvp::label::{recommended_labels, role_group_selector}, product_logging::framework::{ValidatedContainerLogConfigChoice, VectorContainerLogConfig}, role_group_utils::ResourceNames, + role_utils, types::{ kubernetes::{ ConfigMapName, ListenerClassName, ListenerName, NamespaceName, SecretName, Uid, @@ -63,6 +65,9 @@ pub mod build; pub mod dereference; pub mod validate; +// Placeholder version label value for resources whose labels must not change after deployment. +stackable_operator::constant!(UNVERSIONED_PRODUCT_VERSION: ProductVersion = "none"); + /// Every Kubernetes resource produced by the build step. pub struct KubernetesResources { pub stateful_sets: Vec, @@ -70,6 +75,8 @@ pub struct KubernetesResources { pub listeners: Vec, pub config_maps: Vec, pub pod_disruption_budgets: Vec, + pub service_accounts: Vec, + pub role_bindings: Vec, } /// Per-role configuration extracted during validation. @@ -319,6 +326,15 @@ impl ValidatedCluster { } } + /// Type-safe names for the per-cluster RBAC resources: the ServiceAccount shared by all + /// Pods, its (namespaced) RoleBinding, and the operator-deployed ClusterRole it binds. + pub fn rbac_resource_names(&self) -> role_utils::ResourceNames { + role_utils::ResourceNames { + cluster_name: self.name.clone(), + product_name: product_name(), + } + } + /// Type-safe names for the resources of a role group. pub fn resource_names( &self, @@ -333,12 +349,21 @@ impl ValidatedCluster { } /// Recommended labels for a role-group resource. + /// The type-safe role name for an Airflow role. + /// + /// Infallible: every `AirflowRole` serialises to a short, valid role name. + pub fn role_name(role: &AirflowRole) -> RoleName { + role.to_string() + .parse() + .expect("an AirflowRole serialises to a valid RoleName") + } + pub fn recommended_labels( &self, role: &AirflowRole, role_group_name: &RoleGroupName, ) -> Labels { - self.recommended_labels_for(&role.role_name(), role_group_name) + self.recommended_labels_for(&Self::role_name(role), role_group_name) } /// Recommended labels for a resource that is not tied to a concrete [`AirflowRole`] (e.g. the @@ -351,16 +376,16 @@ impl ValidatedCluster { self.recommended_labels_with(&self.product_version, role_name, role_group_name) } - /// Recommended labels with a constant `none` version, for PVC templates that cannot be modified - /// after deployment (keeps the labels stable across version upgrades). + /// Recommended labels with the constant [`UNVERSIONED_PRODUCT_VERSION`], for PVC templates + /// that cannot be modified after deployment (keeps the labels stable across version upgrades). pub fn unversioned_recommended_labels( &self, role: &AirflowRole, role_group_name: &RoleGroupName, ) -> Labels { self.recommended_labels_with( - &ProductVersion::from_str("none").expect("'none' is a valid product version"), - &role.role_name(), + &UNVERSIONED_PRODUCT_VERSION, + &Self::role_name(role), role_group_name, ) } @@ -388,7 +413,12 @@ impl ValidatedCluster { role: &AirflowRole, role_group_name: &RoleGroupName, ) -> Labels { - role_group_selector(self, &product_name(), &role.role_name(), role_group_name) + role_group_selector( + self, + &product_name(), + &Self::role_name(role), + role_group_name, + ) } /// Returns an [`ObjectMetaBuilder`] pre-filled with the namespace, the resource `name`, an owner diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index 8da1ef1a..961c073d 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -731,15 +731,6 @@ impl AirflowRole { } } - /// The role name as a type-safe label/resource-name value. - /// - /// Infallible: every `AirflowRole` serialises to a short, valid role name. - pub fn role_name(&self) -> stackable_operator::v2::types::operator::RoleName { - self.to_string() - .parse() - .expect("an AirflowRole serialises to a valid RoleName") - } - pub fn listener_class_name( &self, airflow: &v1alpha2::AirflowCluster, From 6866383191e79a7ac4c86af4fb147e2b4e73efd5 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 20 Jul 2026 13:06:01 +0200 Subject: [PATCH 2/8] reference PR branch --- Cargo.lock | 9 ++++++++ Cargo.nix | 56 +++++++++++++++++++++++------------------------ Cargo.toml | 4 ++-- crate-hashes.json | 18 +++++++-------- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d2fd279..0539068b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1607,6 +1607,7 @@ dependencies = [ [[package]] name = "k8s-version" version = "0.1.3" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "darling", "regex", @@ -3037,6 +3038,7 @@ dependencies = [ [[package]] name = "stackable-certs" version = "0.4.1" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "const-oid", "ecdsa", @@ -3060,6 +3062,7 @@ dependencies = [ [[package]] name = "stackable-operator" version = "0.113.4" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "base64", "clap", @@ -3104,6 +3107,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.3.1" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "darling", "proc-macro2", @@ -3114,6 +3118,7 @@ dependencies = [ [[package]] name = "stackable-shared" version = "0.1.2" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "jiff", "k8s-openapi", @@ -3130,6 +3135,7 @@ dependencies = [ [[package]] name = "stackable-telemetry" version = "0.6.5" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "axum", "clap", @@ -3153,6 +3159,7 @@ dependencies = [ [[package]] name = "stackable-versioned" version = "0.11.1" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "kube", "schemars", @@ -3166,6 +3173,7 @@ dependencies = [ [[package]] name = "stackable-versioned-macros" version = "0.11.1" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "convert_case", "convert_case_extras", @@ -3183,6 +3191,7 @@ dependencies = [ [[package]] name = "stackable-webhook" version = "0.9.2" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#ccd92a67d821373e16530e35c3e58197d9fd030b" dependencies = [ "arc-swap", "async-trait", diff --git a/Cargo.nix b/Cargo.nix index 9d5b9ce3..231b58eb 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -5107,9 +5107,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "k8s_version"; authors = [ @@ -9994,9 +9994,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_certs"; authors = [ @@ -10093,13 +10093,13 @@ rec { }; "stackable-operator" = rec { crateName = "stackable-operator"; - version = "0.113.3"; + version = "0.113.4"; edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_operator"; authors = [ @@ -10296,9 +10296,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; procMacro = true; libName = "stackable_operator_derive"; @@ -10331,9 +10331,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_shared"; authors = [ @@ -10412,9 +10412,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_telemetry"; authors = [ @@ -10522,9 +10522,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_versioned"; authors = [ @@ -10572,9 +10572,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; procMacro = true; libName = "stackable_versioned_macros"; @@ -10640,9 +10640,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech/operator-rs.git"; - rev = "013bbf43f7006a4ddfc08a147f68441ed88b462b"; - sha256 = "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps"; + url = "https://github.com/stackabletech//operator-rs.git"; + rev = "ccd92a67d821373e16530e35c3e58197d9fd030b"; + sha256 = "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0"; }; libName = "stackable_webhook"; authors = [ diff --git a/Cargo.toml b/Cargo.toml index f7e9583f..40ca1005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,5 +33,5 @@ tokio = { version = "1.52", features = ["full"] } tracing = "0.1" [patch."https://github.com/stackabletech/operator-rs.git"] -# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" } -stackable-operator = { path = "../operator-rs/crates/stackable-operator" } +stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/smooth-operator/build-rbac" } +# stackable-operator = { path = "../operator-rs/crates/stackable-operator" } diff --git a/crate-hashes.json b/crate-hashes.json index cd3917c3..56842431 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -1,12 +1,12 @@ { - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#k8s-version@0.1.3": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-certs@0.4.1": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-operator-derive@0.3.1": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-operator@0.113.3": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-shared@0.1.2": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-telemetry@0.6.5": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-versioned-macros@0.11.1": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-versioned@0.11.1": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.113.3#stackable-webhook@0.9.2": "054p2mcinq3x4iykmc72nhga7kapc8ihkx9mnzqq3qdm2bk2qcps", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#k8s-version@0.1.3": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-certs@0.4.1": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-operator-derive@0.3.1": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-operator@0.113.4": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-shared@0.1.2": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-telemetry@0.6.5": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-versioned-macros@0.11.1": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-versioned@0.11.1": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", + "git+https://github.com/stackabletech//operator-rs.git?branch=feat%2Fsmooth-operator%2Fbuild-rbac#stackable-webhook@0.9.2": "0221ixvlanpcfjygqk4bpwkikydw6385gc4yiyi9r4mw6r1r8iq0", "git+https://github.com/stackabletech/product-config.git?tag=0.8.0#product-config@0.8.0": "1dz70kapm2wdqcr7ndyjji0lhsl98bsq95gnb2lw487wf6yr7987" } \ No newline at end of file From 39df8dfc5b02efb56727ae69aebbf81e5637d354 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Tue, 21 Jul 2026 17:54:07 +0200 Subject: [PATCH 3/8] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681fbcad..ff0445f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,11 @@ - Internal operator refactoring: introduce a build() step in the reconciler that assembles all relevant Kubernetes resources before anything is applied ([#814]). +- The RBAC ServiceAccount and RoleBinding are now built with the operator-rs `v2::rbac` + functions and carry the full set of recommended labels ([#821]). [#814]: https://github.com/stackabletech/airflow-operator/pull/814 +[#821]: https://github.com/stackabletech/airflow-operator/pull/821 ## [26.7.0] - 2026-07-21 From ce02972ce74eecf96ebc05f25b353aa97a922a1c Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 22 Jul 2026 12:18:17 +0200 Subject: [PATCH 4/8] added test to verify parsing of all roles --- rust/operator-binary/src/controller/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rust/operator-binary/src/controller/mod.rs b/rust/operator-binary/src/controller/mod.rs index 5869173e..659c6782 100644 --- a/rust/operator-binary/src/controller/mod.rs +++ b/rust/operator-binary/src/controller/mod.rs @@ -526,3 +526,20 @@ impl HasUid for ValidatedCluster { self.uid.clone() } } + +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use super::ValidatedCluster; + use crate::crd::AirflowRole; + + /// Locks the invariant behind the `expect` in [`ValidatedCluster::role_name`]: every + /// `AirflowRole` variant (present and future) must serialise to a valid `RoleName`. + #[test] + fn every_airflow_role_serialises_to_a_valid_role_name() { + for role in AirflowRole::iter() { + ValidatedCluster::role_name(&role); + } + } +} From 856af927084b2106c7e83d3d86a24e6d18d6c26a Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 22 Jul 2026 12:28:14 +0200 Subject: [PATCH 5/8] rename constant --- .../operator-binary/src/controller/build/resource/listener.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/operator-binary/src/controller/build/resource/listener.rs b/rust/operator-binary/src/controller/build/resource/listener.rs index 5d56eb32..e42d3b25 100644 --- a/rust/operator-binary/src/controller/build/resource/listener.rs +++ b/rust/operator-binary/src/controller/build/resource/listener.rs @@ -15,7 +15,7 @@ use crate::{ // The group listener is a role-level object, so a constant `none` role-group is used as the // role-group label value. -stackable_operator::constant!(PLACEHOLDER_LISTENER_ROLE_GROUP: RoleGroupName = "none"); +stackable_operator::constant!(NONE_ROLE_GROUP_NAME: RoleGroupName = "none"); pub fn build_group_listener( cluster: &ValidatedCluster, @@ -29,7 +29,7 @@ pub fn build_group_listener( listener_group_name, cluster.recommended_labels_for( &ValidatedCluster::role_name(role), - &PLACEHOLDER_LISTENER_ROLE_GROUP, + &NONE_ROLE_GROUP_NAME, ), ) .build(), From 9ef8a4b7c5cf511b0f03491f50e6503a656bea5f Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 22 Jul 2026 14:16:53 +0200 Subject: [PATCH 6/8] renamed resource names functions for clarity --- .../src/controller/build/resource/config_map.rs | 2 +- .../src/controller/build/resource/executor.rs | 9 ++++++--- .../src/controller/build/resource/rbac.rs | 4 ++-- .../src/controller/build/resource/service.rs | 6 +++--- .../src/controller/build/resource/statefulset.rs | 4 ++-- rust/operator-binary/src/controller/mod.rs | 4 ++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/rust/operator-binary/src/controller/build/resource/config_map.rs b/rust/operator-binary/src/controller/build/resource/config_map.rs index 23bb2dbd..d4d10958 100644 --- a/rust/operator-binary/src/controller/build/resource/config_map.rs +++ b/rust/operator-binary/src/controller/build/resource/config_map.rs @@ -66,7 +66,7 @@ pub fn build_rolegroup_config_map( validated_cluster .object_meta( validated_cluster - .resource_names(role_name, role_group_name) + .role_group_resource_names(role_name, role_group_name) .role_group_config_map() .to_string(), validated_cluster.recommended_labels_for(role_name, role_group_name), diff --git a/rust/operator-binary/src/controller/build/resource/executor.rs b/rust/operator-binary/src/controller/build/resource/executor.rs index 91eaa03f..3bcc320a 100644 --- a/rust/operator-binary/src/controller/build/resource/executor.rs +++ b/rust/operator-binary/src/controller/build/resource/executor.rs @@ -100,7 +100,7 @@ pub fn build_executor_template_config_map( .affinity(&executor_config.affinity) .service_account_name( cluster - .rbac_resource_names() + .cluster_resource_names() .service_account_name() .to_string(), ) @@ -156,7 +156,7 @@ pub fn build_executor_template_config_map( .context(AddVolumeSnafu)?; pb.add_volumes(volumes::create_volumes( cluster - .resource_names(&executor_role_name(), &executor_role_group_name()) + .role_group_resource_names(&executor_role_name(), &executor_role_group_name()) .role_group_config_map() .as_ref(), &executor_config.logging.product_container, @@ -167,7 +167,10 @@ pub fn build_executor_template_config_map( pb.add_container(build_logging_container( resolved_product_image, vector_log_config, - &cluster.resource_names(&executor_role_name(), &executor_template_role_group_name()), + &cluster.role_group_resource_names( + &executor_role_name(), + &executor_template_role_group_name(), + ), )); } diff --git a/rust/operator-binary/src/controller/build/resource/rbac.rs b/rust/operator-binary/src/controller/build/resource/rbac.rs index a60485bd..7bbb3aa7 100644 --- a/rust/operator-binary/src/controller/build/resource/rbac.rs +++ b/rust/operator-binary/src/controller/build/resource/rbac.rs @@ -21,7 +21,7 @@ stackable_operator::constant!(NONE_ROLE_GROUP_NAME: RoleGroupName = "none"); pub fn build_service_account(cluster: &ValidatedCluster) -> ServiceAccount { rbac::build_service_account( cluster, - &cluster.rbac_resource_names(), + &cluster.cluster_resource_names(), rbac_labels(cluster), ) } @@ -31,7 +31,7 @@ pub fn build_service_account(cluster: &ValidatedCluster) -> ServiceAccount { pub fn build_role_binding(cluster: &ValidatedCluster) -> RoleBinding { rbac::build_role_binding( cluster, - &cluster.rbac_resource_names(), + &cluster.cluster_resource_names(), rbac_labels(cluster), ) } diff --git a/rust/operator-binary/src/controller/build/resource/service.rs b/rust/operator-binary/src/controller/build/resource/service.rs index 66c273c6..4bba1114 100644 --- a/rust/operator-binary/src/controller/build/resource/service.rs +++ b/rust/operator-binary/src/controller/build/resource/service.rs @@ -22,7 +22,7 @@ pub fn build_rolegroup_headless_service( metadata: cluster .object_meta( cluster - .resource_names(&ValidatedCluster::role_name(role), role_group_name) + .role_group_resource_names(&ValidatedCluster::role_name(role), role_group_name) .headless_service_name() .to_string(), cluster.recommended_labels(role, role_group_name), @@ -51,7 +51,7 @@ pub fn build_rolegroup_metrics_service( metadata: cluster .object_meta( cluster - .resource_names(&ValidatedCluster::role_name(role), role_group_name) + .role_group_resource_names(&ValidatedCluster::role_name(role), role_group_name) .metrics_service_name() .to_string(), cluster.recommended_labels(role, role_group_name), @@ -85,7 +85,7 @@ pub fn stateful_set_service_name( ) -> Option { Some( cluster - .resource_names(&ValidatedCluster::role_name(role), role_group_name) + .role_group_resource_names(&ValidatedCluster::role_name(role), role_group_name) .headless_service_name() .to_string(), ) diff --git a/rust/operator-binary/src/controller/build/resource/statefulset.rs b/rust/operator-binary/src/controller/build/resource/statefulset.rs index 8a8b1065..5e2aa8d8 100644 --- a/rust/operator-binary/src/controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/controller/build/resource/statefulset.rs @@ -116,7 +116,7 @@ pub fn build_server_rolegroup_statefulset( let mut pb = PodBuilder::new(); let resource_names = validated_cluster - .resource_names(&ValidatedCluster::role_name(airflow_role), role_group_name); + .role_group_resource_names(&ValidatedCluster::role_name(airflow_role), role_group_name); let recommended_object_labels = validated_cluster.recommended_labels(airflow_role, role_group_name); @@ -141,7 +141,7 @@ pub fn build_server_rolegroup_statefulset( .affinity(&merged_airflow_config.affinity) .service_account_name( validated_cluster - .rbac_resource_names() + .cluster_resource_names() .service_account_name() .to_string(), ) diff --git a/rust/operator-binary/src/controller/mod.rs b/rust/operator-binary/src/controller/mod.rs index 659c6782..923ae89b 100644 --- a/rust/operator-binary/src/controller/mod.rs +++ b/rust/operator-binary/src/controller/mod.rs @@ -328,7 +328,7 @@ impl ValidatedCluster { /// Type-safe names for the per-cluster RBAC resources: the ServiceAccount shared by all /// Pods, its (namespaced) RoleBinding, and the operator-deployed ClusterRole it binds. - pub fn rbac_resource_names(&self) -> role_utils::ResourceNames { + pub fn cluster_resource_names(&self) -> role_utils::ResourceNames { role_utils::ResourceNames { cluster_name: self.name.clone(), product_name: product_name(), @@ -336,7 +336,7 @@ impl ValidatedCluster { } /// Type-safe names for the resources of a role group. - pub fn resource_names( + pub fn role_group_resource_names( &self, role_name: &RoleName, role_group_name: &RoleGroupName, From bc8ab6cebaae7c4b5fd3a9782bac8fcfaacbf7ca Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 22 Jul 2026 16:54:54 +0200 Subject: [PATCH 7/8] bump to op-rs 0.114 --- Cargo.lock | 20 ++++++++--------- Cargo.nix | 56 +++++++++++++++++++++++------------------------ Cargo.toml | 4 ++-- crate-hashes.json | 18 +++++++-------- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67b21e07..a48f780d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1581,7 +1581,7 @@ dependencies = [ [[package]] name = "k8s-version" version = "0.1.3" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "darling", "regex", @@ -2975,7 +2975,7 @@ dependencies = [ [[package]] name = "stackable-certs" version = "0.4.1" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "const-oid", "ecdsa", @@ -2998,8 +2998,8 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.113.4" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +version = "0.114.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "base64", "clap", @@ -3043,7 +3043,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.3.1" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "darling", "proc-macro2", @@ -3054,7 +3054,7 @@ dependencies = [ [[package]] name = "stackable-shared" version = "0.1.2" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "jiff", "k8s-openapi", @@ -3071,7 +3071,7 @@ dependencies = [ [[package]] name = "stackable-telemetry" version = "0.6.5" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "axum", "clap", @@ -3095,7 +3095,7 @@ dependencies = [ [[package]] name = "stackable-versioned" version = "0.11.1" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "kube", "schemars", @@ -3109,7 +3109,7 @@ dependencies = [ [[package]] name = "stackable-versioned-macros" version = "0.11.1" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "convert_case", "convert_case_extras", @@ -3127,7 +3127,7 @@ dependencies = [ [[package]] name = "stackable-webhook" version = "0.9.2" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=main#dff9ff89576bb7676de5eda90318d488b728637f" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#ee66b8d66fe10c216acfed0f81070b362604b392" dependencies = [ "arc-swap", "async-trait", diff --git a/Cargo.nix b/Cargo.nix index cb88c70b..bc70959a 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -5026,9 +5026,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "k8s_version"; authors = [ @@ -9793,9 +9793,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_certs"; authors = [ @@ -9892,13 +9892,13 @@ rec { }; "stackable-operator" = rec { crateName = "stackable-operator"; - version = "0.113.4"; + version = "0.114.0"; edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_operator"; authors = [ @@ -10091,9 +10091,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; procMacro = true; libName = "stackable_operator_derive"; @@ -10126,9 +10126,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_shared"; authors = [ @@ -10207,9 +10207,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_telemetry"; authors = [ @@ -10317,9 +10317,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_versioned"; authors = [ @@ -10367,9 +10367,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; procMacro = true; libName = "stackable_versioned_macros"; @@ -10435,9 +10435,9 @@ rec { edition = "2024"; workspace_member = null; src = pkgs.fetchgit { - url = "https://github.com/stackabletech//operator-rs.git"; - rev = "dff9ff89576bb7676de5eda90318d488b728637f"; - sha256 = "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx"; + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "ee66b8d66fe10c216acfed0f81070b362604b392"; + sha256 = "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj"; }; libName = "stackable_webhook"; authors = [ diff --git a/Cargo.toml b/Cargo.toml index 523384ea..912ac226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/stackabletech/airflow-operator" stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", features = [ "crds", "webhook", -], tag = "stackable-operator-0.113.3" } +], tag = "stackable-operator-0.114.0" } anyhow = "1.0" built = { version = "0.8", features = ["chrono", "git2"] } @@ -33,5 +33,5 @@ tokio = { version = "1.52", features = ["full"] } tracing = "0.1" [patch."https://github.com/stackabletech/operator-rs.git"] -stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" } +# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" } # stackable-operator = { path = "../operator-rs/crates/stackable-operator" } diff --git a/crate-hashes.json b/crate-hashes.json index cfe2345f..04979b96 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -1,11 +1,11 @@ { - "git+https://github.com/stackabletech//operator-rs.git?branch=main#k8s-version@0.1.3": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-certs@0.4.1": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-operator-derive@0.3.1": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-operator@0.113.4": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-shared@0.1.2": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-telemetry@0.6.5": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-versioned-macros@0.11.1": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-versioned@0.11.1": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx", - "git+https://github.com/stackabletech//operator-rs.git?branch=main#stackable-webhook@0.9.2": "1w18996a9wqljknv36g1nglkk17hz4hf7zdrjh0rrr64aw9dxdpx" + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#k8s-version@0.1.3": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-certs@0.4.1": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-operator-derive@0.3.1": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-operator@0.114.0": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-shared@0.1.2": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-telemetry@0.6.5": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-versioned-macros@0.11.1": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-versioned@0.11.1": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.114.0#stackable-webhook@0.9.2": "1v6slybgc0xqsmh3bxyid6xjvmz8ps41nfmmc6csgyzqs2v0wzxj" } \ No newline at end of file From 4b07c893b9a1bd76f734725e0ece08219f1d6aa1 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 22 Jul 2026 18:25:31 +0200 Subject: [PATCH 8/8] linting --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index def0c340..365bf054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,6 @@ [#821]: https://github.com/stackabletech/airflow-operator/pull/821 [#827]: https://github.com/stackabletech/airflow-operator/pull/827 - ## [26.7.0] - 2026-07-21 ## [26.7.0-rc1] - 2026-07-16