@@ -5,8 +5,11 @@ use stackable_operator::{
55 builder:: {
66 meta:: ObjectMetaBuilder ,
77 pod:: {
8- PodBuilder , container:: ContainerBuilder , resources:: ResourceRequirementsBuilder ,
9- security:: PodSecurityContextBuilder , volume:: VolumeBuilder ,
8+ PodBuilder ,
9+ container:: { ContainerBuilder , FieldPathEnvVar } ,
10+ resources:: ResourceRequirementsBuilder ,
11+ security:: PodSecurityContextBuilder ,
12+ volume:: VolumeBuilder ,
1013 } ,
1114 } ,
1215 commons:: product_image_selection:: ResolvedProductImage ,
@@ -16,9 +19,8 @@ use stackable_operator::{
1619 api:: {
1720 apps:: v1:: { StatefulSet , StatefulSetSpec , StatefulSetUpdateStrategy } ,
1821 core:: v1:: {
19- ConfigMapKeySelector , ConfigMapVolumeSource , ContainerPort , EnvVar , EnvVarSource ,
20- ExecAction , ObjectFieldSelector , PodSpec , Probe , ServiceAccount , TCPSocketAction ,
21- Volume ,
22+ ConfigMapVolumeSource , ContainerPort , EnvVar , EnvVarSource , ExecAction ,
23+ ObjectFieldSelector , PodSpec , Probe , ServiceAccount , TCPSocketAction , Volume ,
2224 } ,
2325 } ,
2426 apimachinery:: pkg:: { apis:: meta:: v1:: LabelSelector , util:: intstr:: IntOrString } ,
@@ -29,7 +31,7 @@ use stackable_operator::{
2931 builder:: {
3032 meta:: ownerreference_from_resource,
3133 pod:: {
32- container:: EnvVarSet ,
34+ container:: { EnvVarName , EnvVarSet } ,
3335 volume:: { ListenerReference , listener_operator_volume_source_builder_build_pvc} ,
3436 } ,
3537 } ,
@@ -38,7 +40,7 @@ use stackable_operator::{
3840 STACKABLE_LOG_DIR , ValidatedContainerLogConfigChoice , vector_container,
3941 } ,
4042 role_group_utils:: ResourceNames ,
41- types:: kubernetes:: { ContainerName , PersistentVolumeClaimName , VolumeName } ,
43+ types:: kubernetes:: { ConfigMapKey , ContainerName , PersistentVolumeClaimName , VolumeName } ,
4244 } ,
4345} ;
4446
@@ -84,6 +86,46 @@ stackable_operator::constant!(VECTOR_LOG_VOLUME_NAME: VolumeName = "log");
8486/// Name of both the env var and the ZooKeeper discovery ConfigMap key holding the
8587/// ZooKeeper connection string.
8688const ZOOKEEPER_ENV_VAR_NAME : & str = "ZOOKEEPER" ;
89+
90+ /// Parses a compile-time-known env var name; panics only on a programming error (a malformed
91+ /// literal in this file).
92+ fn env_var_name ( name : & str ) -> EnvVarName {
93+ EnvVarName :: from_str ( name) . expect ( "a static env var name is valid" )
94+ }
95+
96+ /// Environment variables the operator sets on the Kafka container that are common to broker and
97+ /// controller role groups.
98+ ///
99+ /// These form the base; the caller merges the user's `envOverrides` on top (see
100+ /// [`build_broker_rolegroup_statefulset`] and [`build_controller_rolegroup_statefulset`]), so a
101+ /// user override wins on a name collision. Using an [`EnvVarSet`] (a name-keyed map) makes that
102+ /// precedence explicit and de-duplicates by name, rather than relying on append order.
103+ fn common_operator_env_vars (
104+ validated_cluster : & ValidatedCluster ,
105+ kafka_security : & ValidatedKafkaSecurity ,
106+ ) -> EnvVarSet {
107+ let mut env = EnvVarSet :: new ( )
108+ . with_field_path ( & env_var_name ( "POD_NAME" ) , & FieldPathEnvVar :: Name )
109+ . with_value (
110+ & env_var_name ( "KAFKA_CLIENT_PORT" ) ,
111+ kafka_security. client_port ( ) . to_string ( ) ,
112+ ) ;
113+
114+ // Present in ZooKeeper mode only: brokers use it to connect, controllers for migration.
115+ if let Some ( zookeeper_config_map_name) =
116+ & validated_cluster. cluster_config . zookeeper_config_map_name
117+ {
118+ env = env. with_config_map_key_ref (
119+ & env_var_name ( ZOOKEEPER_ENV_VAR_NAME ) ,
120+ zookeeper_config_map_name,
121+ & ConfigMapKey :: from_str ( ZOOKEEPER_ENV_VAR_NAME )
122+ . expect ( "a static config map key is valid" ) ,
123+ ) ;
124+ }
125+
126+ env
127+ }
128+
87129const POD_MANAGEMENT_POLICY_PARALLEL : & str = "Parallel" ;
88130
89131#[ derive( Snafu , Debug ) ]
@@ -212,36 +254,10 @@ pub fn build_broker_rolegroup_statefulset(
212254 . context ( AddKerberosConfigSnafu ) ?;
213255 }
214256
215- let mut env = Vec :: < EnvVar > :: from ( validated_rg. env_overrides . clone ( ) ) ;
216-
217- if let Some ( zookeeper_config_map_name) =
218- & validated_cluster. cluster_config . zookeeper_config_map_name
219- {
220- env. push ( EnvVar {
221- name : ZOOKEEPER_ENV_VAR_NAME . to_string ( ) ,
222- value_from : Some ( EnvVarSource {
223- config_map_key_ref : Some ( ConfigMapKeySelector {
224- name : zookeeper_config_map_name. to_string ( ) ,
225- key : ZOOKEEPER_ENV_VAR_NAME . to_string ( ) ,
226- ..ConfigMapKeySelector :: default ( )
227- } ) ,
228- ..EnvVarSource :: default ( )
229- } ) ,
230- ..EnvVar :: default ( )
231- } )
232- } ;
233-
234- env. push ( EnvVar {
235- name : "POD_NAME" . to_string ( ) ,
236- value_from : Some ( EnvVarSource {
237- field_ref : Some ( ObjectFieldSelector {
238- api_version : Some ( "v1" . to_string ( ) ) ,
239- field_path : "metadata.name" . to_string ( ) ,
240- } ) ,
241- ..EnvVarSource :: default ( )
242- } ) ,
243- ..EnvVar :: default ( )
244- } ) ;
257+ // Operator-set env vars first; the user's `envOverrides` are merged on top and win.
258+ let env: Vec < EnvVar > = common_operator_env_vars ( validated_cluster, kafka_security)
259+ . merge ( validated_rg. env_overrides . clone ( ) )
260+ . into ( ) ;
245261
246262 cb_kafka
247263 . image_from_product_image ( resolved_product_image)
@@ -274,10 +290,6 @@ pub fn build_broker_rolegroup_statefulset(
274290 ) ?;
275291
276292 cb_kafka
277- . add_env_var (
278- "KAFKA_CLIENT_PORT" . to_string ( ) ,
279- kafka_security. client_port ( ) . to_string ( ) ,
280- )
281293 . add_env_vars ( env)
282294 . add_container_ports ( container_ports ( kafka_security) )
283295 . add_volume_mount ( LOG_DIRS_VOLUME_NAME , STACKABLE_DATA_DIR )
@@ -458,67 +470,21 @@ pub fn build_controller_rolegroup_statefulset(
458470
459471 let mut pod_builder = PodBuilder :: new ( ) ;
460472
461- let mut env = Vec :: < EnvVar > :: from ( validated_rg. env_overrides . clone ( ) ) ;
462-
463- env. push ( EnvVar {
464- name : "NAMESPACE" . to_string ( ) ,
465- value_from : Some ( EnvVarSource {
466- field_ref : Some ( ObjectFieldSelector {
467- api_version : Some ( "v1" . to_string ( ) ) ,
468- field_path : "metadata.namespace" . to_string ( ) ,
469- } ) ,
470- ..EnvVarSource :: default ( )
471- } ) ,
472- ..EnvVar :: default ( )
473- } ) ;
474-
475- env. push ( EnvVar {
476- name : "POD_NAME" . to_string ( ) ,
477- value_from : Some ( EnvVarSource {
478- field_ref : Some ( ObjectFieldSelector {
479- api_version : Some ( "v1" . to_string ( ) ) ,
480- field_path : "metadata.name" . to_string ( ) ,
481- } ) ,
482- ..EnvVarSource :: default ( )
483- } ) ,
484- ..EnvVar :: default ( )
485- } ) ;
486-
487- env. push ( EnvVar {
488- name : "ROLEGROUP_HEADLESS_SERVICE_NAME" . to_string ( ) ,
489- value : Some ( resource_names. headless_service_name ( ) . to_string ( ) ) ,
490- ..EnvVar :: default ( )
491- } ) ;
492-
493- env. push ( EnvVar {
494- name : "CLUSTER_DOMAIN" . to_string ( ) ,
495- value : Some ( validated_cluster. cluster_domain . to_string ( ) ) ,
496- ..EnvVar :: default ( )
497- } ) ;
498-
499- env. push ( EnvVar {
500- name : "KAFKA_CLIENT_PORT" . to_string ( ) ,
501- value : Some ( kafka_security. client_port ( ) . to_string ( ) ) ,
502- ..EnvVar :: default ( )
503- } ) ;
504-
505- // Controllers need the ZooKeeper connection string for migration
506- if let Some ( zookeeper_config_map_name) =
507- & validated_cluster. cluster_config . zookeeper_config_map_name
508- {
509- env. push ( EnvVar {
510- name : ZOOKEEPER_ENV_VAR_NAME . to_string ( ) ,
511- value_from : Some ( EnvVarSource {
512- config_map_key_ref : Some ( ConfigMapKeySelector {
513- name : zookeeper_config_map_name. to_string ( ) ,
514- key : ZOOKEEPER_ENV_VAR_NAME . to_string ( ) ,
515- ..ConfigMapKeySelector :: default ( )
516- } ) ,
517- ..EnvVarSource :: default ( )
518- } ) ,
519- ..EnvVar :: default ( )
520- } )
521- } ;
473+ // Operator-set env vars first (common + controller-specific); the user's `envOverrides`
474+ // are merged on top and win.
475+ let env: Vec < EnvVar > = common_operator_env_vars ( validated_cluster, kafka_security)
476+ . with_field_path ( & env_var_name ( "NAMESPACE" ) , & FieldPathEnvVar :: Namespace )
477+ . with_value (
478+ & env_var_name ( "ROLEGROUP_HEADLESS_SERVICE_NAME" ) ,
479+ resource_names. headless_service_name ( ) . to_string ( ) ,
480+ )
481+ . with_value (
482+ & env_var_name ( "CLUSTER_DOMAIN" ) ,
483+ validated_cluster. cluster_domain . to_string ( ) ,
484+ )
485+ . with_value ( & env_var_name ( "PRE_STOP_CONTROLLER_SLEEP_SECONDS" ) , "10" )
486+ . merge ( validated_rg. env_overrides . clone ( ) )
487+ . into ( ) ;
522488
523489 cb_kafka
524490 . image_from_product_image ( resolved_product_image)
@@ -534,8 +500,7 @@ pub fn build_controller_rolegroup_statefulset(
534500 . pod_descriptors( Some ( kafka_role) )
535501 . context( BuildPodDescriptorsSnafu ) ?,
536502 & resolved_product_image. product_version,
537- ) ] )
538- . add_env_var ( "PRE_STOP_CONTROLLER_SLEEP_SECONDS" , "10" ) ;
503+ ) ] ) ;
539504
540505 add_common_kafka_env (
541506 & mut cb_kafka,
0 commit comments