33
44use std:: str:: FromStr ;
55
6- use stackable_operator:: v2:: types:: common:: Port ;
6+ use snafu:: { ResultExt , Snafu } ;
7+ use stackable_operator:: { utils:: cluster_info:: KubernetesClusterInfo , v2:: types:: common:: Port } ;
78
8- use crate :: controller:: RoleGroupName ;
9+ use crate :: controller:: {
10+ KubernetesResources , RoleGroupName , ValidatedCluster ,
11+ build:: resource:: {
12+ config_map:: build_rolegroup_config_map,
13+ daemonset:: build_server_rolegroup_daemonset,
14+ discovery:: build_discovery_config_map,
15+ service:: {
16+ build_rolegroup_headless_service, build_rolegroup_metrics_service,
17+ build_server_role_service,
18+ } ,
19+ } ,
20+ } ;
921
1022pub mod properties;
1123pub mod resource;
1224
25+ #[ derive( Snafu , Debug ) ]
26+ pub enum Error {
27+ #[ snafu( display( "failed to build ConfigMap for role group {role_group}" ) ) ]
28+ ConfigMap {
29+ source : resource:: config_map:: Error ,
30+ role_group : RoleGroupName ,
31+ } ,
32+
33+ #[ snafu( display( "failed to build DaemonSet for role group {role_group}" ) ) ]
34+ DaemonSet {
35+ source : resource:: daemonset:: Error ,
36+ role_group : RoleGroupName ,
37+ } ,
38+
39+ #[ snafu( display( "failed to build the discovery ConfigMap" ) ) ]
40+ Discovery { source : resource:: discovery:: Error } ,
41+ }
42+
43+ /// Builds every Kubernetes resource for the given validated cluster.
44+ ///
45+ /// Does not need a Kubernetes client: every reference to another Kubernetes resource is already
46+ /// dereferenced and validated by this point, so the errors returned here are resource-assembly
47+ /// failures only.
48+ ///
49+ /// `service_account_name` is the name of the RBAC `ServiceAccount` the Pods run under (the RBAC
50+ /// resources are built and applied separately, in the reconcile step). `cluster_info` supplies the
51+ /// Kubernetes cluster domain used in the discovery URL and the sidecar environment.
52+ pub fn build (
53+ cluster : & ValidatedCluster ,
54+ service_account_name : & str ,
55+ opa_bundle_builder_image : & str ,
56+ user_info_fetcher_image : & str ,
57+ cluster_info : & KubernetesClusterInfo ,
58+ ) -> Result < KubernetesResources , Error > {
59+ let mut daemon_sets = vec ! [ ] ;
60+ let mut services = vec ! [ ] ;
61+ let mut config_maps = vec ! [ ] ;
62+
63+ // The role-level load-balanced Service, which is not bound to a single role group.
64+ services. push ( build_server_role_service ( cluster) ) ;
65+
66+ for role_group_configs in cluster. role_group_configs . values ( ) {
67+ for ( role_group_name, role_group) in role_group_configs {
68+ config_maps. push (
69+ build_rolegroup_config_map ( cluster, role_group_name, role_group) . context (
70+ ConfigMapSnafu {
71+ role_group : role_group_name. clone ( ) ,
72+ } ,
73+ ) ?,
74+ ) ;
75+ services. push ( build_rolegroup_headless_service ( cluster, role_group_name) ) ;
76+ services. push ( build_rolegroup_metrics_service ( cluster, role_group_name) ) ;
77+ daemon_sets. push (
78+ build_server_rolegroup_daemonset (
79+ cluster,
80+ role_group_name,
81+ role_group,
82+ opa_bundle_builder_image,
83+ user_info_fetcher_image,
84+ service_account_name,
85+ cluster_info,
86+ )
87+ . context ( DaemonSetSnafu {
88+ role_group : role_group_name. clone ( ) ,
89+ } ) ?,
90+ ) ;
91+ }
92+ }
93+
94+ // The cluster-level discovery ConfigMap.
95+ config_maps. push ( build_discovery_config_map ( cluster, cluster_info) . context ( DiscoverySnafu ) ?) ;
96+
97+ Ok ( KubernetesResources {
98+ daemon_sets,
99+ services,
100+ config_maps,
101+ } )
102+ }
103+
13104/// The port the bundle-builder sidecar listens on, and which OPA connects to for bundle polling.
14105pub ( crate ) const BUNDLE_BUILDER_PORT : Port = Port ( 3030 ) ;
15106
@@ -21,3 +112,71 @@ stackable_operator::constant!(pub(crate) PLACEHOLDER_ROLE_LEVEL_ROLE_GROUP: Role
21112// Placeholder role-group name for the recommended labels of the discovery `ConfigMap`, which is a
22113// cluster-level object not bound to a single role group.
23114stackable_operator:: constant!( pub ( crate ) PLACEHOLDER_DISCOVERY_ROLE_GROUP : RoleGroupName = "discovery" ) ;
115+
116+ #[ cfg( test) ]
117+ mod tests {
118+ use serde_json:: json;
119+ use stackable_operator:: {
120+ commons:: networking:: DomainName , kube:: Resource , utils:: cluster_info:: KubernetesClusterInfo ,
121+ } ;
122+
123+ use super :: build;
124+ use crate :: controller:: {
125+ ValidatedCluster , build:: properties:: test_support:: validated_cluster_from_spec,
126+ } ;
127+
128+ fn cluster_info ( ) -> KubernetesClusterInfo {
129+ KubernetesClusterInfo {
130+ cluster_domain : DomainName :: try_from ( "cluster.local" ) . unwrap ( ) ,
131+ }
132+ }
133+
134+ fn sorted_names ( resources : & [ impl Resource ] ) -> Vec < & str > {
135+ let mut names: Vec < & str > = resources
136+ . iter ( )
137+ . filter_map ( |resource| resource. meta ( ) . name . as_deref ( ) )
138+ . collect ( ) ;
139+ names. sort ( ) ;
140+ names
141+ }
142+
143+ /// A validated single-role-group cluster with no TLS or extra sidecars.
144+ fn cluster ( ) -> ValidatedCluster {
145+ validated_cluster_from_spec ( json ! ( {
146+ "image" : { "productVersion" : "1.2.3" } ,
147+ "servers" : { "roleGroups" : { "default" : { } } } ,
148+ } ) )
149+ }
150+
151+ #[ test]
152+ fn build_produces_expected_resource_names ( ) {
153+ let resources = build (
154+ & cluster ( ) ,
155+ "test-opa-serviceaccount" ,
156+ "bundle-builder-image" ,
157+ "user-info-fetcher-image" ,
158+ & cluster_info ( ) ,
159+ )
160+ . expect ( "build succeeds" ) ;
161+
162+ // One DaemonSet per role group.
163+ assert_eq ! (
164+ sorted_names( & resources. daemon_sets) ,
165+ [ "test-opa-server-default" ]
166+ ) ;
167+ // The role-level Service plus a headless and a metrics Service per role group.
168+ assert_eq ! (
169+ sorted_names( & resources. services) ,
170+ [
171+ "test-opa-server" ,
172+ "test-opa-server-default-headless" ,
173+ "test-opa-server-default-metrics" ,
174+ ]
175+ ) ;
176+ // The per-role-group ConfigMap plus the cluster-level discovery ConfigMap (`test-opa`).
177+ assert_eq ! (
178+ sorted_names( & resources. config_maps) ,
179+ [ "test-opa" , "test-opa-server-default" ]
180+ ) ;
181+ }
182+ }
0 commit comments