@@ -21,6 +21,11 @@ use stackable_operator::{
2121 } ,
2222 crd:: { listener:: v1alpha1:: Listener , s3} ,
2323 database_connections:: drivers:: jdbc:: JdbcDatabaseConnectionDetails ,
24+ k8s_openapi:: api:: {
25+ apps:: v1:: StatefulSet ,
26+ core:: v1:: { ConfigMap , Service } ,
27+ policy:: v1:: PodDisruptionBudget ,
28+ } ,
2429 kube:: {
2530 Resource , ResourceExt ,
2631 api:: ObjectMeta ,
@@ -49,12 +54,7 @@ use strum::EnumDiscriminants;
4954
5055use crate :: {
5156 OPERATOR_NAME ,
52- controller:: build:: resource:: {
53- discovery,
54- listener:: build_role_listener,
55- pdb:: build_pdb,
56- service:: { build_rolegroup_headless_service, build_rolegroup_metrics_service} ,
57- } ,
57+ controller:: build:: resource:: discovery,
5858 crd:: { APP_NAME , HdfsConnection , HiveClusterStatus , HiveRole , MetaStoreConfig , v1alpha1} ,
5959} ;
6060
@@ -68,30 +68,13 @@ pub struct Ctx {
6868
6969#[ derive( Snafu , Debug , EnumDiscriminants ) ]
7070#[ strum_discriminants( derive( strum:: IntoStaticStr ) ) ]
71- #[ allow( clippy:: enum_variant_names) ]
7271pub enum Error {
73- #[ snafu( display( "failed to apply Service for role group {role_group}" ) ) ]
74- ApplyRoleGroupService {
75- source : stackable_operator:: cluster_resources:: Error ,
76- role_group : RoleGroupName ,
77- } ,
78-
79- #[ snafu( display( "failed to build ConfigMap for role group {role_group}" ) ) ]
80- BuildRoleGroupConfigMap {
81- source : build:: resource:: config_map:: Error ,
82- role_group : RoleGroupName ,
83- } ,
72+ #[ snafu( display( "failed to build the Kubernetes resources" ) ) ]
73+ BuildResources { source : build:: Error } ,
8474
85- #[ snafu( display( "failed to apply ConfigMap for role group {role_group} " ) ) ]
86- ApplyRoleGroupConfig {
75+ #[ snafu( display( "failed to apply Kubernetes resource " ) ) ]
76+ ApplyResource {
8777 source : stackable_operator:: cluster_resources:: Error ,
88- role_group : RoleGroupName ,
89- } ,
90-
91- #[ snafu( display( "failed to apply StatefulSet for role group {role_group}" ) ) ]
92- ApplyRoleGroupStatefulSet {
93- source : stackable_operator:: cluster_resources:: Error ,
94- role_group : RoleGroupName ,
9578 } ,
9679
9780 #[ snafu( display( "failed to build discovery ConfigMap" ) ) ]
@@ -127,11 +110,6 @@ pub enum Error {
127110 source : stackable_operator:: commons:: rbac:: Error ,
128111 } ,
129112
130- #[ snafu( display( "failed to apply PodDisruptionBudget" ) ) ]
131- ApplyPdb {
132- source : stackable_operator:: cluster_resources:: Error ,
133- } ,
134-
135113 #[ snafu( display( "failed to get required Labels" ) ) ]
136114 GetRequiredLabels {
137115 source :
@@ -143,24 +121,13 @@ pub enum Error {
143121 source : error_boundary:: InvalidObject ,
144122 } ,
145123
146- #[ snafu( display( "failed to apply group listener for {role}" ) ) ]
147- ApplyGroupListener {
148- source : stackable_operator:: cluster_resources:: Error ,
149- role : String ,
150- } ,
151124 #[ snafu( display( "failed to dereference cluster resources" ) ) ]
152125 Dereference {
153126 source : crate :: controller:: dereference:: Error ,
154127 } ,
155128
156129 #[ snafu( display( "failed to validate cluster configuration" ) ) ]
157130 Validate { source : validate:: Error } ,
158-
159- #[ snafu( display( "failed to build StatefulSet for role group {role_group}" ) ) ]
160- BuildRoleGroupStatefulSet {
161- source : build:: resource:: statefulset:: Error ,
162- role_group : RoleGroupName ,
163- } ,
164131}
165132type Result < T , E = Error > = std:: result:: Result < T , E > ;
166133
@@ -462,6 +429,19 @@ pub struct ValidatedRoleConfig {
462429 pub listener_class : ListenerClassName ,
463430}
464431
432+ /// Every Kubernetes resource produced by the client-free [`build`](build::build) step.
433+ ///
434+ /// The role-level discovery `ConfigMap` is deliberately absent: it is built from the *applied*
435+ /// role [`Listener`]'s ingress addresses, so it is assembled in the reconcile step after the
436+ /// Listener has been applied, not in the build step.
437+ pub struct KubernetesResources {
438+ pub stateful_sets : Vec < StatefulSet > ,
439+ pub services : Vec < Service > ,
440+ pub listeners : Vec < Listener > ,
441+ pub config_maps : Vec < ConfigMap > ,
442+ pub pod_disruption_budgets : Vec < PodDisruptionBudget > ,
443+ }
444+
465445pub async fn reconcile_hive (
466446 hive : Arc < DeserializeGuard < v1alpha1:: HiveCluster > > ,
467447 ctx : Arc < Ctx > ,
@@ -515,98 +495,79 @@ pub async fn reconcile_hive(
515495 . await
516496 . context ( ApplyRoleBindingSnafu ) ?;
517497
518- let mut ss_cond_builder = StatefulSetConditionBuilder :: default ( ) ;
498+ // The ServiceAccount name is deterministic on the built object, so the client-free build step
499+ // does not depend on the applied ServiceAccount.
500+ let service_account_name = rbac_sa. name_any ( ) ;
519501
520- for ( hive_role, role_group_configs) in & validated_cluster. role_group_configs {
521- for ( role_group_name, rg) in role_group_configs {
522- let rg_metrics_service =
523- build_rolegroup_metrics_service ( & validated_cluster, role_group_name) ;
502+ let resources = build:: build (
503+ & validated_cluster,
504+ & client. kubernetes_cluster_info ,
505+ & service_account_name,
506+ )
507+ . context ( BuildResourcesSnafu ) ?;
524508
525- let rg_headless_service =
526- build_rolegroup_headless_service ( & validated_cluster, role_group_name) ;
509+ let mut ss_cond_builder = StatefulSetConditionBuilder :: default ( ) ;
527510
528- let rg_configmap = build:: resource:: config_map:: build_metastore_rolegroup_config_map (
529- & validated_cluster,
530- & client. kubernetes_cluster_info ,
531- role_group_name,
532- rg,
533- )
534- . with_context ( |_| BuildRoleGroupConfigMapSnafu {
535- role_group : role_group_name. clone ( ) ,
536- } ) ?;
537-
538- let rg_statefulset =
539- build:: resource:: statefulset:: build_metastore_rolegroup_statefulset (
540- hive_role,
541- & validated_cluster,
542- role_group_name,
543- rg,
544- & rbac_sa. name_any ( ) ,
545- )
546- . with_context ( |_| BuildRoleGroupStatefulSetSnafu {
547- role_group : role_group_name. clone ( ) ,
548- } ) ?;
511+ // Apply order: everything before StatefulSets, StatefulSets last. A StatefulSet must only be
512+ // applied after all ConfigMaps and Secrets it mounts, to prevent unnecessary Pod restarts.
513+ // See https://github.com/stackabletech/commons-operator/issues/111 for details.
514+ for service in resources. services {
515+ cluster_resources
516+ . add ( client, service)
517+ . await
518+ . context ( ApplyResourceSnafu ) ?;
519+ }
549520
521+ // The role Listener is applied before the discovery ConfigMap, which is built below from the
522+ // applied Listener's ingress addresses. Hive has a single role Listener, so at most one is
523+ // captured here.
524+ let mut applied_role_listener: Option < Listener > = None ;
525+ for listener in resources. listeners {
526+ applied_role_listener = Some (
550527 cluster_resources
551- . add ( client, rg_metrics_service )
528+ . add ( client, listener )
552529 . await
553- . context ( ApplyRoleGroupServiceSnafu {
554- role_group : role_group_name. clone ( ) ,
555- } ) ?;
530+ . context ( ApplyResourceSnafu ) ?,
531+ ) ;
532+ }
533+
534+ for config_map in resources. config_maps {
535+ cluster_resources
536+ . add ( client, config_map)
537+ . await
538+ . context ( ApplyResourceSnafu ) ?;
539+ }
556540
541+ for pdb in resources. pod_disruption_budgets {
542+ cluster_resources
543+ . add ( client, pdb)
544+ . await
545+ . context ( ApplyResourceSnafu ) ?;
546+ }
547+
548+ for statefulset in resources. stateful_sets {
549+ ss_cond_builder. add (
557550 cluster_resources
558- . add ( client, rg_headless_service )
551+ . add ( client, statefulset )
559552 . await
560- . context ( ApplyRoleGroupServiceSnafu {
561- role_group : role_group_name. clone ( ) ,
562- } ) ?;
563-
564- cluster_resources. add ( client, rg_configmap) . await . context (
565- ApplyRoleGroupConfigSnafu {
566- role_group : role_group_name. clone ( ) ,
567- } ,
568- ) ?;
569-
570- // Note: The StatefulSet needs to be applied after all ConfigMaps and Secrets it
571- // mounts to prevent unnecessary Pod restarts.
572- // See https://github.com/stackabletech/commons-operator/issues/111 for details.
573- ss_cond_builder. add (
574- cluster_resources
575- . add ( client, rg_statefulset)
576- . await
577- . context ( ApplyRoleGroupStatefulSetSnafu {
578- role_group : role_group_name. clone ( ) ,
579- } ) ?,
580- ) ;
581- }
553+ . context ( ApplyResourceSnafu ) ?,
554+ ) ;
582555 }
583556
557+ // The discovery ConfigMap is built from the *applied* role Listener's ingress addresses, so it
558+ // is assembled here rather than in the client-free build step. Its applied resource version
559+ // feeds the status discovery hash.
584560 // std's SipHasher is deprecated, and DefaultHasher is unstable across Rust releases.
585561 // We don't /need/ stability, but it's still nice to avoid spurious changes where possible.
586562 let mut discovery_hash = FnvHasher :: with_key ( 0 ) ;
587563
588- if let Some ( role_config) = & validated_cluster. role_config {
589- if let Some ( pdb) = build_pdb ( & role_config. pdb , & validated_cluster, & HiveRole :: MetaStore ) {
590- cluster_resources
591- . add ( client, pdb)
592- . await
593- . context ( ApplyPdbSnafu ) ?;
594- }
595-
596- let role_listener: Listener = build_role_listener (
564+ if let Some ( role_listener) = applied_role_listener {
565+ let discovery_cm = discovery:: build_discovery_configmap (
597566 & validated_cluster,
598- & HiveRole :: MetaStore ,
599- & role_config. listener_class ,
600- ) ;
601- let listener = cluster_resources. add ( client, role_listener) . await . context (
602- ApplyGroupListenerSnafu {
603- role : HiveRole :: MetaStore . to_string ( ) ,
604- } ,
605- ) ?;
606-
607- let discovery_cm =
608- discovery:: build_discovery_configmap ( & validated_cluster, HiveRole :: MetaStore , listener)
609- . context ( BuildDiscoveryConfigSnafu ) ?;
567+ HiveRole :: MetaStore ,
568+ role_listener,
569+ )
570+ . context ( BuildDiscoveryConfigSnafu ) ?;
610571 let discovery_cm = cluster_resources
611572 . add ( client, discovery_cm)
612573 . await
0 commit comments