@@ -11,6 +11,7 @@ use crate::{
1111 executor:: build_executor_template_config_map,
1212 listener:: build_group_listener,
1313 pdb:: build_pdb,
14+ rbac:: { build_role_binding, build_service_account} ,
1415 service:: { build_rolegroup_headless_service, build_rolegroup_metrics_service} ,
1516 statefulset:: build_server_rolegroup_statefulset,
1617 } ,
@@ -47,14 +48,7 @@ pub enum Error {
4748/// Does not need a Kubernetes client: every reference to another Kubernetes resource is already
4849/// dereferenced and validated by this point. Cluster configuration is likewise already validated,
4950/// so the errors returned here are resource-assembly failures only.
50- ///
51- /// `service_account_name` is the name of the RBAC `ServiceAccount` the role-group Pods and the
52- /// Kubernetes-executor pod template run under (RBAC resources are built and applied separately,
53- /// in the reconcile step).
54- pub fn build (
55- cluster : & ValidatedCluster ,
56- service_account_name : & str ,
57- ) -> Result < KubernetesResources , Error > {
51+ pub fn build ( cluster : & ValidatedCluster ) -> Result < KubernetesResources , Error > {
5852 let mut stateful_sets = vec ! [ ] ;
5953 let mut services = vec ! [ ] ;
6054 let mut listeners = vec ! [ ] ;
@@ -81,7 +75,6 @@ pub fn build(
8175
8276 let executor_template_config_map = build_executor_template_config_map (
8377 cluster,
84- service_account_name,
8578 & executor_template. config ,
8679 & executor_template. env_overrides ,
8780 & executor_template. pod_overrides ,
@@ -123,7 +116,7 @@ pub fn build(
123116 config_maps. push (
124117 config_map:: build_rolegroup_config_map (
125118 cluster,
126- & role . role_name ( ) ,
119+ & ValidatedCluster :: role_name ( role ) ,
127120 role_group_name,
128121 & rg_config. config_overrides ,
129122 logging,
@@ -140,7 +133,6 @@ pub fn build(
140133 role_group_name,
141134 rg_config,
142135 logging,
143- service_account_name,
144136 )
145137 . context ( StatefulSetSnafu {
146138 role_group : role_group_name. clone ( ) ,
@@ -155,11 +147,15 @@ pub fn build(
155147 listeners,
156148 config_maps,
157149 pod_disruption_budgets,
150+ service_accounts : vec ! [ build_service_account( cluster) ] ,
151+ role_bindings : vec ! [ build_role_binding( cluster) ] ,
158152 } )
159153}
160154
161155#[ cfg( test) ]
162156mod tests {
157+ use std:: collections:: BTreeMap ;
158+
163159 use stackable_operator:: kube:: Resource ;
164160
165161 use super :: build;
@@ -184,7 +180,7 @@ mod tests {
184180 apiVersion: airflow.stackable.tech/v1alpha2
185181 kind: AirflowCluster
186182 metadata:
187- name: airflow
183+ name: my- airflow
188184 namespace: default
189185 uid: e6ac237d-a6d4-43a1-8135-f36506110912
190186 spec:
@@ -263,25 +259,84 @@ mod tests {
263259 #[ test]
264260 fn build_produces_expected_resource_names ( ) {
265261 let cluster = celery_executor_cluster ( ) ;
266- let resources = build ( & cluster, "airflow-serviceaccount" ) . expect ( "build succeeds" ) ;
262+ let resources = build ( & cluster) . expect ( "build succeeds" ) ;
267263
268264 assert_eq ! (
269265 sorted_names( & resources. stateful_sets) ,
270- [ "airflow-scheduler-default" , "airflow-webserver-default" ]
266+ [
267+ "my-airflow-scheduler-default" ,
268+ "my-airflow-webserver-default"
269+ ]
271270 ) ;
272271 // One headless and one metrics Service per role group.
273272 assert_eq ! ( resources. services. len( ) , 4 ) ;
274273 assert_eq ! (
275274 sorted_names( & resources. config_maps) ,
276- [ "airflow-scheduler-default" , "airflow-webserver-default" ]
275+ [
276+ "my-airflow-scheduler-default" ,
277+ "my-airflow-webserver-default"
278+ ]
277279 ) ;
278280 // The webserver is the only role with a group Listener.
279- assert_eq ! ( sorted_names( & resources. listeners) , [ "airflow-webserver" ] ) ;
281+ assert_eq ! ( sorted_names( & resources. listeners) , [ "my- airflow-webserver" ] ) ;
280282 // A default PDB per role (the Celery worker included).
281283 assert_eq ! (
282284 sorted_names( & resources. pod_disruption_budgets) ,
283- [ "airflow-scheduler" , "airflow-webserver" , "airflow-worker" ]
285+ [
286+ "my-airflow-scheduler" ,
287+ "my-airflow-webserver" ,
288+ "my-airflow-worker"
289+ ]
290+ ) ;
291+ }
292+
293+ /// Locks the RBAC resource names, the roleRef, and the recommended label set against
294+ /// accidental drift. The fixture's cluster name deliberately differs from the product name so
295+ /// that swapped `name`/`instance` label values cannot pass unnoticed.
296+ #[ test]
297+ fn build_produces_rbac ( ) {
298+ let cluster = celery_executor_cluster ( ) ;
299+ let resources = build ( & cluster) . expect ( "build succeeds" ) ;
300+
301+ assert_eq ! (
302+ sorted_names( & resources. service_accounts) ,
303+ [ "my-airflow-serviceaccount" ]
304+ ) ;
305+ assert_eq ! (
306+ sorted_names( & resources. role_bindings) ,
307+ [ "my-airflow-rolebinding" ]
308+ ) ;
309+
310+ let expected_labels = BTreeMap :: from (
311+ [
312+ ( "app.kubernetes.io/component" , "none" ) ,
313+ ( "app.kubernetes.io/instance" , "my-airflow" ) ,
314+ (
315+ "app.kubernetes.io/managed-by" ,
316+ "airflow.stackable.tech_airflowcluster" ,
317+ ) ,
318+ ( "app.kubernetes.io/name" , "airflow" ) ,
319+ ( "app.kubernetes.io/role-group" , "none" ) ,
320+ ( "app.kubernetes.io/version" , "3.1.6-stackable0.0.0-dev" ) ,
321+ ( "stackable.tech/vendor" , "Stackable" ) ,
322+ ]
323+ . map ( |( key, value) | ( key. to_string ( ) , value. to_string ( ) ) ) ,
284324 ) ;
325+ let service_account = resources
326+ . service_accounts
327+ . first ( )
328+ . expect ( "a ServiceAccount is built" ) ;
329+ assert_eq ! (
330+ service_account. metadata. labels,
331+ Some ( expected_labels. clone( ) )
332+ ) ;
333+
334+ let role_binding = resources
335+ . role_bindings
336+ . first ( )
337+ . expect ( "a RoleBinding is built" ) ;
338+ assert_eq ! ( role_binding. metadata. labels, Some ( expected_labels) ) ;
339+ assert_eq ! ( role_binding. role_ref. name, "airflow-clusterrole" ) ;
285340 }
286341
287342 /// The Kubernetes-executor branch of `build()` (moved here from `reconcile`) additionally emits
@@ -290,15 +345,15 @@ mod tests {
290345 #[ test]
291346 fn build_kubernetes_executor_adds_pod_template_config_maps ( ) {
292347 let cluster = kubernetes_executor_cluster ( ) ;
293- let resources = build ( & cluster, "airflow-serviceaccount" ) . expect ( "build succeeds" ) ;
348+ let resources = build ( & cluster) . expect ( "build succeeds" ) ;
294349
295350 assert_eq ! (
296351 sorted_names( & resources. config_maps) ,
297352 [
298- "airflow-executor-kubernetes" ,
299- "airflow-executor-pod-template" ,
300- "airflow-scheduler-default" ,
301- "airflow-webserver-default" ,
353+ "my- airflow-executor-kubernetes" ,
354+ "my- airflow-executor-pod-template" ,
355+ "my- airflow-scheduler-default" ,
356+ "my- airflow-webserver-default" ,
302357 ]
303358 ) ;
304359 }
0 commit comments