33
44use std:: str:: FromStr ;
55
6- use stackable_operator:: v2:: types:: operator:: RoleGroupName ;
6+ use snafu:: { ResultExt , Snafu } ;
7+ use stackable_operator:: {
8+ utils:: cluster_info:: KubernetesClusterInfo , v2:: types:: operator:: RoleGroupName ,
9+ } ;
10+
11+ use crate :: {
12+ controller:: {
13+ KubernetesResources , ValidatedCluster ,
14+ build:: resource:: {
15+ config_map:: { self , build_rolegroup_config_map} ,
16+ discovery:: { self , build_discovery_config_map} ,
17+ pdb:: build_pdb,
18+ service:: { build_rolegroup_metrics_service, build_rolegroup_service} ,
19+ statefulset:: { self , build_rolegroup_statefulset} ,
20+ } ,
21+ } ,
22+ crd:: HbaseRole ,
23+ } ;
724
825// Placeholder role-group name used for the recommended labels of the role-level discovery
926// `ConfigMap` (which is not tied to a single role group).
1027stackable_operator:: constant!( pub ( crate ) PLACEHOLDER_DISCOVERY_ROLE_GROUP : RoleGroupName = "discovery" ) ;
1128
29+ #[ derive( Snafu , Debug ) ]
30+ pub enum Error {
31+ #[ snafu( display( "failed to build ConfigMap for role {hbase_role} role group {role_group}" ) ) ]
32+ ConfigMap {
33+ source : config_map:: Error ,
34+ hbase_role : HbaseRole ,
35+ role_group : RoleGroupName ,
36+ } ,
37+
38+ #[ snafu( display( "failed to build StatefulSet for role {hbase_role} role group {role_group}" ) ) ]
39+ StatefulSet {
40+ source : statefulset:: Error ,
41+ hbase_role : HbaseRole ,
42+ role_group : RoleGroupName ,
43+ } ,
44+
45+ #[ snafu( display( "failed to build discovery ConfigMap" ) ) ]
46+ Discovery { source : discovery:: Error } ,
47+ }
48+
49+ /// Builds every Kubernetes resource for the given validated cluster.
50+ ///
51+ /// Does not need a Kubernetes client: every reference to another Kubernetes resource is already
52+ /// dereferenced and validated by this point, so the errors returned here are resource-assembly
53+ /// failures only. `cluster_info` is static cluster metadata (not a client call), and
54+ /// `service_account_name` is the name of the RBAC `ServiceAccount` the role-group Pods run under
55+ /// (RBAC resources are built and applied separately, in the reconcile step).
56+ pub fn build (
57+ cluster : & ValidatedCluster ,
58+ cluster_info : & KubernetesClusterInfo ,
59+ service_account_name : & str ,
60+ ) -> Result < KubernetesResources , Error > {
61+ let mut stateful_sets = vec ! [ ] ;
62+ let mut services = vec ! [ ] ;
63+ let mut config_maps = vec ! [ ] ;
64+ let mut pod_disruption_budgets = vec ! [ ] ;
65+
66+ for ( hbase_role, role_group_configs) in & cluster. role_group_configs {
67+ for ( role_group_name, rg_config) in role_group_configs {
68+ services. push ( build_rolegroup_service (
69+ cluster,
70+ hbase_role,
71+ role_group_name,
72+ ) ) ;
73+ services. push ( build_rolegroup_metrics_service (
74+ cluster,
75+ hbase_role,
76+ role_group_name,
77+ ) ) ;
78+ config_maps. push (
79+ build_rolegroup_config_map ( cluster, cluster_info, hbase_role, role_group_name)
80+ . with_context ( |_| ConfigMapSnafu {
81+ hbase_role : hbase_role. clone ( ) ,
82+ role_group : role_group_name. clone ( ) ,
83+ } ) ?,
84+ ) ;
85+ stateful_sets. push (
86+ build_rolegroup_statefulset (
87+ cluster,
88+ hbase_role,
89+ role_group_name,
90+ rg_config,
91+ service_account_name,
92+ )
93+ . with_context ( |_| StatefulSetSnafu {
94+ hbase_role : hbase_role. clone ( ) ,
95+ role_group : role_group_name. clone ( ) ,
96+ } ) ?,
97+ ) ;
98+ }
99+
100+ if let Some ( role_config) = cluster. role_configs . get ( hbase_role)
101+ && let Some ( pdb) = build_pdb ( & role_config. pdb , cluster, hbase_role)
102+ {
103+ pod_disruption_budgets. push ( pdb) ;
104+ }
105+ }
106+
107+ // The role-level discovery ConfigMap advertises the cluster's connection information; it is
108+ // deterministic (derived only from the validated cluster and static cluster info).
109+ config_maps. push ( build_discovery_config_map ( cluster, cluster_info) . context ( DiscoverySnafu ) ?) ;
110+
111+ Ok ( KubernetesResources {
112+ stateful_sets,
113+ services,
114+ config_maps,
115+ pod_disruption_budgets,
116+ } )
117+ }
118+
12119pub mod graceful_shutdown;
13120pub mod jvm;
14121pub mod kerberos;
@@ -17,3 +124,66 @@ pub mod properties;
17124pub mod region_mover;
18125pub mod resource;
19126pub mod role;
127+
128+ #[ cfg( test) ]
129+ mod tests {
130+ use stackable_operator:: kube:: Resource ;
131+
132+ use super :: build;
133+ use crate :: test_utils;
134+
135+ /// Collects the `.metadata.name`s of the given resources, sorted for stable comparison.
136+ fn sorted_names ( resources : & [ impl Resource ] ) -> Vec < & str > {
137+ let mut names: Vec < & str > = resources
138+ . iter ( )
139+ . filter_map ( |resource| resource. meta ( ) . name . as_deref ( ) )
140+ . collect ( ) ;
141+ names. sort ( ) ;
142+ names
143+ }
144+
145+ #[ test]
146+ fn build_produces_expected_resource_names ( ) {
147+ let cluster = test_utils:: validated_cluster ( ) ;
148+ let cluster_info = test_utils:: cluster_info ( ) ;
149+ let resources =
150+ build ( & cluster, & cluster_info, "hbase-serviceaccount" ) . expect ( "build succeeds" ) ;
151+
152+ // One StatefulSet per role group (one `default` group for each of the three roles).
153+ assert_eq ! (
154+ sorted_names( & resources. stateful_sets) ,
155+ [
156+ "hbase-master-default" ,
157+ "hbase-regionserver-default" ,
158+ "hbase-restserver-default" ,
159+ ]
160+ ) ;
161+ // One headless and one metrics Service per role group.
162+ assert_eq ! (
163+ sorted_names( & resources. services) ,
164+ [
165+ "hbase-master-default-headless" ,
166+ "hbase-master-default-metrics" ,
167+ "hbase-regionserver-default-headless" ,
168+ "hbase-regionserver-default-metrics" ,
169+ "hbase-restserver-default-headless" ,
170+ "hbase-restserver-default-metrics" ,
171+ ]
172+ ) ;
173+ // One ConfigMap per role group plus the cluster-wide discovery ConfigMap (`hbase`).
174+ assert_eq ! (
175+ sorted_names( & resources. config_maps) ,
176+ [
177+ "hbase" ,
178+ "hbase-master-default" ,
179+ "hbase-regionserver-default" ,
180+ "hbase-restserver-default" ,
181+ ]
182+ ) ;
183+ // A default PodDisruptionBudget per role.
184+ assert_eq ! (
185+ sorted_names( & resources. pod_disruption_budgets) ,
186+ [ "hbase-master" , "hbase-regionserver" , "hbase-restserver" ]
187+ ) ;
188+ }
189+ }
0 commit comments