@@ -3,11 +3,17 @@ use std::{hash::Hasher, str::FromStr, sync::Arc};
33
44use const_format:: concatcp;
55use fnv:: FnvHasher ;
6- use snafu:: { ResultExt , Snafu } ;
6+ use snafu:: { OptionExt , ResultExt , Snafu } ;
77use stackable_operator:: {
88 cli:: OperatorEnvironmentOptions ,
99 cluster_resources:: ClusterResourceApplyStrategy ,
1010 commons:: rbac:: build_rbac_resources,
11+ crd:: listener:: v1alpha1:: Listener ,
12+ k8s_openapi:: api:: {
13+ apps:: v1:: StatefulSet ,
14+ core:: v1:: { ConfigMap , Service } ,
15+ policy:: v1:: PodDisruptionBudget ,
16+ } ,
1117 kube:: {
1218 api:: DynamicObject ,
1319 core:: { DeserializeGuard , error_boundary} ,
@@ -20,26 +26,15 @@ use stackable_operator::{
2026 compute_conditions, operations:: ClusterOperationsConditionBuilder ,
2127 statefulset:: StatefulSetConditionBuilder ,
2228 } ,
23- v2:: {
24- cluster_resources:: cluster_resources_new,
25- types:: operator:: { ControllerName , RoleGroupName } ,
26- } ,
29+ v2:: { cluster_resources:: cluster_resources_new, types:: operator:: ControllerName } ,
2730} ;
2831use strum:: { EnumDiscriminants , IntoStaticStr } ;
2932
3033use crate :: {
3134 APP_NAME , OPERATOR_NAME , ObjectRef ,
32- crd:: { ZookeeperRole , v1alpha1} ,
35+ crd:: v1alpha1,
3336 zk_controller:: {
34- build:: resource:: {
35- config_map, discovery,
36- listener:: build_role_listener,
37- pdb:: build_pdb,
38- service:: {
39- build_server_rolegroup_headless_service, build_server_rolegroup_metrics_service,
40- } ,
41- statefulset:: build_server_rolegroup_statefulset,
42- } ,
37+ build:: resource:: discovery,
4338 validate:: { operator_name, product_name} ,
4439 } ,
4540} ;
@@ -62,7 +57,6 @@ type Result<T, E = Error> = std::result::Result<T, E>;
6257
6358#[ derive( Snafu , Debug , EnumDiscriminants ) ]
6459#[ strum_discriminants( derive( IntoStaticStr ) ) ]
65- #[ allow( clippy:: enum_variant_names) ]
6660pub enum Error {
6761 #[ snafu( display( "ZookeeperCluster object is invalid" ) ) ]
6862 InvalidZookeeperCluster {
@@ -81,41 +75,24 @@ pub enum Error {
8175 #[ snafu( display( "internal operator failure" ) ) ]
8276 InternalOperatorFailure { source : crate :: crd:: Error } ,
8377
84- #[ snafu( display( "failed to apply Service for role group {rolegroup}" ) ) ]
85- ApplyRoleGroupService {
86- source : stackable_operator:: cluster_resources:: Error ,
87- rolegroup : RoleGroupName ,
88- } ,
89-
90- #[ snafu( display( "failed to build ConfigMap for role group {rolegroup}" ) ) ]
91- BuildRoleGroupConfigMap {
92- source : config_map:: Error ,
93- rolegroup : RoleGroupName ,
94- } ,
78+ #[ snafu( display( "failed to build the Kubernetes resources" ) ) ]
79+ BuildResources { source : build:: Error } ,
9580
96- #[ snafu( display( "failed to apply ConfigMap for role group {rolegroup} " ) ) ]
97- ApplyRoleGroupConfig {
81+ #[ snafu( display( "failed to apply Kubernetes resource " ) ) ]
82+ ApplyResource {
9883 source : stackable_operator:: cluster_resources:: Error ,
99- rolegroup : RoleGroupName ,
100- } ,
101-
102- #[ snafu( display( "failed to build StatefulSet for role group {rolegroup}" ) ) ]
103- BuildRoleGroupStatefulSet {
104- source : build:: resource:: statefulset:: Error ,
105- rolegroup : RoleGroupName ,
106- } ,
107-
108- #[ snafu( display( "failed to apply StatefulSet for role group {rolegroup}" ) ) ]
109- ApplyRoleGroupStatefulSet {
110- source : stackable_operator:: cluster_resources:: Error ,
111- rolegroup : RoleGroupName ,
11284 } ,
11385
11486 #[ snafu( display( "object is missing metadata to build owner reference" ) ) ]
11587 ObjectMissingMetadataForOwnerRef {
11688 source : stackable_operator:: builder:: meta:: Error ,
11789 } ,
11890
91+ #[ snafu( display(
92+ "no role Listener was applied; the discovery ConfigMap is derived from the applied role Listener"
93+ ) ) ]
94+ NoRoleListener ,
95+
11996 #[ snafu( display( "failed to build discovery ConfigMap" ) ) ]
12097 BuildDiscoveryConfig { source : discovery:: Error } ,
12198
@@ -149,23 +126,13 @@ pub enum Error {
149126 source : stackable_operator:: cluster_resources:: Error ,
150127 } ,
151128
152- #[ snafu( display( "failed to apply PodDisruptionBudget" ) ) ]
153- ApplyPdb {
154- source : stackable_operator:: cluster_resources:: Error ,
155- } ,
156-
157129 #[ snafu( display( "failed to build label" ) ) ]
158130 BuildLabel { source : LabelError } ,
159131
160132 #[ snafu( display( "failed to build object meta data" ) ) ]
161133 ObjectMeta {
162134 source : stackable_operator:: builder:: meta:: Error ,
163135 } ,
164-
165- #[ snafu( display( "failed to apply group listener" ) ) ]
166- ApplyGroupListener {
167- source : stackable_operator:: cluster_resources:: Error ,
168- } ,
169136}
170137
171138impl ReconcilerError for Error {
@@ -180,27 +147,34 @@ impl ReconcilerError for Error {
180147 Error :: ValidateCluster { .. } => None ,
181148 Error :: CrdValidationFailure { .. } => None ,
182149 Error :: InternalOperatorFailure { .. } => None ,
183- Error :: ApplyRoleGroupService { .. } => None ,
184- Error :: BuildRoleGroupConfigMap { .. } => None ,
185- Error :: ApplyRoleGroupConfig { .. } => None ,
186- Error :: BuildRoleGroupStatefulSet { .. } => None ,
187- Error :: ApplyRoleGroupStatefulSet { .. } => None ,
150+ Error :: BuildResources { .. } => None ,
151+ Error :: ApplyResource { .. } => None ,
188152 Error :: ObjectMissingMetadataForOwnerRef { .. } => None ,
153+ Error :: NoRoleListener => None ,
189154 Error :: BuildDiscoveryConfig { .. } => None ,
190155 Error :: ApplyDiscoveryConfig { .. } => None ,
191156 Error :: ApplyStatus { .. } => None ,
192157 Error :: ApplyServiceAccount { .. } => None ,
193158 Error :: ApplyRoleBinding { .. } => None ,
194159 Error :: BuildRbacResources { .. } => None ,
195160 Error :: DeleteOrphans { .. } => None ,
196- Error :: ApplyPdb { .. } => None ,
197161 Error :: BuildLabel { .. } => None ,
198162 Error :: ObjectMeta { .. } => None ,
199- Error :: ApplyGroupListener { .. } => None ,
200163 }
201164 }
202165}
203166
167+ /// Every Kubernetes resource produced by the client-free [`build()`](build::build) step.
168+ ///
169+ /// The discovery `ConfigMap` is deliberately absent — see [`build()`](build::build).
170+ pub struct KubernetesResources {
171+ pub stateful_sets : Vec < StatefulSet > ,
172+ pub services : Vec < Service > ,
173+ pub listeners : Vec < Listener > ,
174+ pub config_maps : Vec < ConfigMap > ,
175+ pub pod_disruption_budgets : Vec < PodDisruptionBudget > ,
176+ }
177+
204178pub async fn reconcile_zk (
205179 zk : Arc < DeserializeGuard < v1alpha1:: ZookeeperCluster > > ,
206180 ctx : Arc < Ctx > ,
@@ -245,7 +219,7 @@ pub async fn reconcile_zk(
245219 . context ( BuildRbacResourcesSnafu ) ?;
246220
247221 cluster_resources
248- . add ( client, rbac_sa. clone ( ) )
222+ . add ( client, rbac_sa)
249223 . await
250224 . context ( ApplyServiceAccountSnafu ) ?;
251225
@@ -254,98 +228,63 @@ pub async fn reconcile_zk(
254228 . await
255229 . context ( ApplyRoleBindingSnafu ) ?;
256230
257- let mut ss_cond_builder = StatefulSetConditionBuilder :: default ( ) ;
231+ let resources = build:: build ( & validated_cluster, & client. kubernetes_cluster_info )
232+ . context ( BuildResourcesSnafu ) ?;
258233
259- let zk_role = ZookeeperRole :: Server ;
260- let server_role_group_configs = validated_cluster
261- . role_group_configs
262- . get ( & zk_role)
263- . into_iter ( )
264- . flatten ( ) ;
265- for ( rolegroup_name, rolegroup_config) in server_role_group_configs {
266- // Resource naming, labels and owner references are derived from the `ValidatedCluster` and
267- // the type-safe `RoleGroupName`.
268- let rg_headless_service =
269- build_server_rolegroup_headless_service ( & validated_cluster, rolegroup_name) ;
270- let rg_metrics_service = build_server_rolegroup_metrics_service (
271- & validated_cluster,
272- rolegroup_name,
273- rolegroup_config,
274- ) ;
275- let rg_configmap = config_map:: build_server_rolegroup_config_map (
276- & validated_cluster,
277- & client. kubernetes_cluster_info ,
278- rolegroup_name,
279- rolegroup_config,
280- )
281- . context ( BuildRoleGroupConfigMapSnafu {
282- rolegroup : rolegroup_name. clone ( ) ,
283- } ) ?;
284- let rg_statefulset = build_server_rolegroup_statefulset (
285- & validated_cluster,
286- rolegroup_name,
287- rolegroup_config,
288- )
289- . with_context ( |_| BuildRoleGroupStatefulSetSnafu {
290- rolegroup : rolegroup_name. clone ( ) ,
291- } ) ?;
234+ let mut ss_cond_builder = StatefulSetConditionBuilder :: default ( ) ;
292235
236+ for service in resources. services {
293237 cluster_resources
294- . add ( client, rg_headless_service)
295- . await
296- . with_context ( |_| ApplyRoleGroupServiceSnafu {
297- rolegroup : rolegroup_name. clone ( ) ,
298- } ) ?;
299- cluster_resources
300- . add ( client, rg_metrics_service)
238+ . add ( client, service)
301239 . await
302- . with_context ( |_| ApplyRoleGroupServiceSnafu {
303- rolegroup : rolegroup_name. clone ( ) ,
304- } ) ?;
305- cluster_resources
306- . add ( client, rg_configmap)
307- . await
308- . with_context ( |_| ApplyRoleGroupConfigSnafu {
309- rolegroup : rolegroup_name. clone ( ) ,
310- } ) ?;
240+ . context ( ApplyResourceSnafu ) ?;
241+ }
311242
312- // Note: The StatefulSet needs to be applied after all ConfigMaps and Secrets it mounts
313- // to prevent unnecessary Pod restarts.
314- // See https://github.com/stackabletech/commons-operator/issues/111 for details.
315- ss_cond_builder . add (
243+ // ZooKeeper has a single role Listener; the applied object feeds the discovery ConfigMap.
244+ let mut applied_role_listener : Option < Listener > = None ;
245+ for listener in resources . listeners {
246+ applied_role_listener = Some (
316247 cluster_resources
317- . add ( client, rg_statefulset )
248+ . add ( client, listener )
318249 . await
319- . with_context ( |_| ApplyRoleGroupStatefulSetSnafu {
320- rolegroup : rolegroup_name. clone ( ) ,
321- } ) ?,
250+ . context ( ApplyResourceSnafu ) ?,
322251 ) ;
323252 }
253+ let role_listener = applied_role_listener. context ( NoRoleListenerSnafu ) ?;
324254
325- if let Some ( role_config) = & validated_cluster. role_config
326- && let Some ( pdb) = build_pdb ( & role_config. pdb , & validated_cluster, & zk_role)
327- {
255+ for config_map in resources. config_maps {
256+ cluster_resources
257+ . add ( client, config_map)
258+ . await
259+ . context ( ApplyResourceSnafu ) ?;
260+ }
261+
262+ for pdb in resources. pod_disruption_budgets {
328263 cluster_resources
329264 . add ( client, pdb)
330265 . await
331- . context ( ApplyPdbSnafu ) ?;
266+ . context ( ApplyResourceSnafu ) ?;
332267 }
333268
334- let listener = build_role_listener ( & validated_cluster, & zk_role) ;
335- let applied_listener = cluster_resources
336- . add ( client, listener)
337- . await
338- . context ( ApplyGroupListenerSnafu ) ?;
269+ // Note: The StatefulSet needs to be applied after all ConfigMaps and Secrets it mounts
270+ // to prevent unnecessary Pod restarts.
271+ // See https://github.com/stackabletech/commons-operator/issues/111 for details.
272+ for statefulset in resources. stateful_sets {
273+ ss_cond_builder. add (
274+ cluster_resources
275+ . add ( client, statefulset)
276+ . await
277+ . context ( ApplyResourceSnafu ) ?,
278+ ) ;
279+ }
339280
340281 // std's SipHasher is deprecated, and DefaultHasher is unstable across Rust releases.
341282 // We don't /need/ stability, but it's still nice to avoid spurious changes where possible.
342283 let mut discovery_hash = FnvHasher :: with_key ( 0 ) ;
343- let discovery_cm = discovery:: build_discovery_configmap (
344- & validated_cluster,
345- ZK_CONTROLLER_NAME ,
346- applied_listener,
347- )
348- . context ( BuildDiscoveryConfigSnafu ) ?;
284+
285+ let discovery_cm =
286+ discovery:: build_discovery_configmap ( & validated_cluster, ZK_CONTROLLER_NAME , role_listener)
287+ . context ( BuildDiscoveryConfigSnafu ) ?;
349288
350289 let discovery_cm = cluster_resources
351290 . add ( client, discovery_cm)
@@ -462,8 +401,13 @@ mod tests {
462401 k8s_openapi:: api:: core:: v1:: ConfigMap , v2:: types:: operator:: RoleGroupName ,
463402 } ;
464403
465- use super :: * ;
466- use crate :: zk_controller:: test_support:: { cluster_info, minimal_zk, validated_cluster} ;
404+ use crate :: {
405+ crd:: ZookeeperRole ,
406+ zk_controller:: {
407+ build:: resource:: config_map,
408+ test_support:: { cluster_info, minimal_zk, validated_cluster} ,
409+ } ,
410+ } ;
467411
468412 #[ test]
469413 fn test_default_config ( ) {
0 commit comments