11//! Builder for role resources
22
3- use std:: str:: FromStr ;
3+ use std:: { collections :: BTreeMap , str:: FromStr } ;
44
55use stackable_operator:: {
66 builder:: meta:: ObjectMetaBuilder ,
@@ -13,7 +13,6 @@ use stackable_operator::{
1313 rbac:: v1:: { ClusterRole , RoleBinding , RoleRef , Subject } ,
1414 } ,
1515 } ,
16- kube:: api:: ObjectMeta ,
1716 kvp:: {
1817 Label , Labels ,
1918 consts:: { STACKABLE_VENDOR_KEY , STACKABLE_VENDOR_VALUE } ,
@@ -23,12 +22,14 @@ use stackable_operator::{
2322use crate :: {
2423 controller:: {
2524 ContextNames , HTTP_PORT , HTTP_PORT_NAME , TRANSPORT_PORT , TRANSPORT_PORT_NAME ,
26- ValidatedCluster , build:: role_group_builder:: RoleGroupBuilder ,
25+ ValidatedCluster , ValidatedSecurity , build:: role_group_builder:: RoleGroupBuilder ,
2726 } ,
27+ crd:: v1alpha1,
2828 framework:: {
2929 NameIsValidLabelValue ,
3030 builder:: {
31- meta:: ownerreference_from_resource, pdb:: pod_disruption_budget_builder_with_role,
31+ meta:: { annotation_ignore_restarter, ownerreference_from_resource} ,
32+ pdb:: pod_disruption_budget_builder_with_role,
3233 } ,
3334 role_utils:: ResourceNames ,
3435 types:: {
@@ -80,7 +81,9 @@ impl<'a> RoleBuilder<'a> {
8081
8182 /// Builds a ServiceAccount used by all role-groups
8283 pub fn build_service_account ( & self ) -> ServiceAccount {
83- let metadata = self . common_metadata ( self . resource_names . service_account_name ( ) ) ;
84+ let metadata = self
85+ . common_metadata ( self . resource_names . service_account_name ( ) )
86+ . build ( ) ;
8487
8588 ServiceAccount {
8689 metadata,
@@ -90,7 +93,9 @@ impl<'a> RoleBuilder<'a> {
9093
9194 /// Builds a RoleBinding used by all role-groups
9295 pub fn build_role_binding ( & self ) -> RoleBinding {
93- let metadata = self . common_metadata ( self . resource_names . role_binding_name ( ) ) ;
96+ let metadata = self
97+ . common_metadata ( self . resource_names . role_binding_name ( ) )
98+ . build ( ) ;
9499
95100 RoleBinding {
96101 metadata,
@@ -116,7 +121,9 @@ impl<'a> RoleBuilder<'a> {
116121 ..ServicePort :: default ( )
117122 } ] ;
118123
119- let metadata = self . common_metadata ( seed_nodes_service_name ( & self . cluster . name ) ) ;
124+ let metadata = self
125+ . common_metadata ( seed_nodes_service_name ( & self . cluster . name ) )
126+ . build ( ) ;
120127
121128 let service_selector =
122129 RoleGroupBuilder :: cluster_manager_labels ( & self . cluster , self . context_names ) ;
@@ -140,7 +147,9 @@ impl<'a> RoleBuilder<'a> {
140147
141148 /// Builds a Listener whose status is used to populate the discovery ConfigMap.
142149 pub fn build_discovery_service_listener ( & self ) -> listener:: v1alpha1:: Listener {
143- let metadata = self . common_metadata ( discovery_service_listener_name ( & self . cluster . name ) ) ;
150+ let metadata = self
151+ . common_metadata ( discovery_service_listener_name ( & self . cluster . name ) )
152+ . build ( ) ;
144153
145154 let listener_class = & self . cluster . role_config . discovery_service_listener_class ;
146155
@@ -166,10 +175,12 @@ impl<'a> RoleBuilder<'a> {
166175 /// The discovery endpoint is derived from the status of the discovery service Listener. If the
167176 /// status is not set yet, the reconciliation process will occur again once the Listener status
168177 /// is updated, leading to the eventual creation of the discovery ConfigMap.
169- pub fn build_discovery_config_map ( & self ) -> Option < ConfigMap > {
178+ pub fn build_maybe_discovery_config_map ( & self ) -> Option < ConfigMap > {
170179 let discovery_endpoint = self . cluster . discovery_endpoint . as_ref ( ) ?;
171180
172- let metadata = self . common_metadata ( discovery_config_map_name ( & self . cluster . name ) ) ;
181+ let metadata = self
182+ . common_metadata ( discovery_config_map_name ( & self . cluster . name ) )
183+ . build ( ) ;
173184
174185 let protocol = if self . cluster . is_server_tls_enabled ( ) {
175186 "https"
@@ -204,6 +215,43 @@ impl<'a> RoleBuilder<'a> {
204215 } )
205216 }
206217
218+ /// Builds the [`ConfigMap`] containing the security configuration files that were defined by
219+ /// value.
220+ ///
221+ /// Returns `None` if the security plugin is disabled or all configuration files are
222+ /// references.
223+ pub fn build_maybe_security_config_map ( & self ) -> Option < ConfigMap > {
224+ let metadata = self
225+ . common_metadata ( security_config_map_name ( & self . cluster . name ) )
226+ . with_annotation ( annotation_ignore_restarter ( ) )
227+ . build ( ) ;
228+
229+ let mut data = BTreeMap :: new ( ) ;
230+
231+ if let ValidatedSecurity :: ManagedByApi { settings, .. }
232+ | ValidatedSecurity :: ManagedByOperator { settings, .. } = & self . cluster . security
233+ {
234+ for file_type in settings {
235+ if let v1alpha1:: SecuritySettingsFileTypeContent :: Value (
236+ v1alpha1:: SecuritySettingsFileTypeContentValue { value } ,
237+ ) = & file_type. content
238+ {
239+ data. insert ( file_type. filename . to_owned ( ) , value. to_string ( ) ) ;
240+ }
241+ }
242+ }
243+
244+ if data. is_empty ( ) {
245+ None
246+ } else {
247+ Some ( ConfigMap {
248+ metadata,
249+ data : Some ( data) ,
250+ ..ConfigMap :: default ( )
251+ } )
252+ }
253+ }
254+
207255 /// Builds a [`PodDisruptionBudget`] used by all role-groups
208256 pub fn build_pdb ( & self ) -> Option < PodDisruptionBudget > {
209257 let pdb_config = & self . cluster . role_config . common . pod_disruption_budget ;
@@ -229,17 +277,20 @@ impl<'a> RoleBuilder<'a> {
229277 }
230278
231279 /// Common metadata for role resources
232- fn common_metadata ( & self , resource_name : impl Into < String > ) -> ObjectMeta {
233- ObjectMetaBuilder :: new ( )
280+ fn common_metadata ( & self , resource_name : impl Into < String > ) -> ObjectMetaBuilder {
281+ let mut builder = ObjectMetaBuilder :: new ( ) ;
282+
283+ builder
234284 . name ( resource_name)
235285 . namespace ( & self . cluster . namespace )
236286 . ownerreference ( ownerreference_from_resource (
237287 & self . cluster ,
238288 None ,
239289 Some ( true ) ,
240290 ) )
241- . with_labels ( self . labels ( ) )
242- . build ( )
291+ . with_labels ( self . labels ( ) ) ;
292+
293+ builder
243294 }
244295
245296 /// Common labels for role resources
@@ -297,6 +348,20 @@ fn discovery_config_map_name(cluster_name: &ClusterName) -> ConfigMapName {
297348 ConfigMapName :: from_str ( cluster_name. as_ref ( ) ) . expect ( "should be a valid ConfigMap name" )
298349}
299350
351+ pub fn security_config_map_name ( cluster_name : & ClusterName ) -> ConfigMapName {
352+ const SUFFIX : & str = "-security-config" ;
353+
354+ // compile-time checks
355+ const _: ( ) = assert ! (
356+ ClusterName :: MAX_LENGTH + SUFFIX . len( ) <= ConfigMapName :: MAX_LENGTH ,
357+ "The string `<cluster_name>-security-config` must not exceed the limit of ConfigMap names."
358+ ) ;
359+ let _ = ClusterName :: IS_RFC_1123_SUBDOMAIN_NAME ;
360+
361+ ConfigMapName :: from_str ( & format ! ( "{}{SUFFIX}" , cluster_name. as_ref( ) ) )
362+ . expect ( "should be a valid ConfigMap name" )
363+ }
364+
300365pub fn discovery_service_listener_name ( cluster_name : & ClusterName ) -> ListenerName {
301366 // compile-time checks
302367 const _: ( ) = assert ! (
@@ -640,12 +705,13 @@ mod tests {
640705 }
641706
642707 #[ test]
643- fn test_build_discovery_config_map ( ) {
708+ fn test_build_maybe_discovery_config_map ( ) {
644709 let context_names = context_names ( ) ;
645710 let role_builder = role_builder ( & context_names) ;
646711
647- let discovery_config_map = serde_json:: to_value ( role_builder. build_discovery_config_map ( ) )
648- . expect ( "should be serializable" ) ;
712+ let discovery_config_map =
713+ serde_json:: to_value ( role_builder. build_maybe_discovery_config_map ( ) )
714+ . expect ( "should be serializable" ) ;
649715
650716 assert_eq ! (
651717 json!( {
@@ -683,6 +749,59 @@ mod tests {
683749 ) ;
684750 }
685751
752+ #[ test]
753+ fn test_build_maybe_security_config_map ( ) {
754+ let context_names = context_names ( ) ;
755+ let role_builder = role_builder ( & context_names) ;
756+
757+ let security_config_map =
758+ serde_json:: to_value ( role_builder. build_maybe_security_config_map ( ) )
759+ . expect ( "should be serializable" ) ;
760+
761+ assert_eq ! (
762+ json!( {
763+ "apiVersion" : "v1" ,
764+ "kind" : "ConfigMap" ,
765+ "metadata" : {
766+ "annotations" : {
767+ "restarter.stackable.tech/ignore" : "true"
768+ } ,
769+ "labels" : {
770+ "app.kubernetes.io/component" : "nodes" ,
771+ "app.kubernetes.io/instance" : "my-opensearch-cluster" ,
772+ "app.kubernetes.io/managed-by" : "opensearch.stackable.tech_opensearchcluster" ,
773+ "app.kubernetes.io/name" : "opensearch" ,
774+ "app.kubernetes.io/version" : "3.4.0" ,
775+ "stackable.tech/vendor" : "Stackable" ,
776+ } ,
777+ "name" : "my-opensearch-cluster-security-config" ,
778+ "namespace" : "default" ,
779+ "ownerReferences" : [
780+ {
781+ "apiVersion" : "opensearch.stackable.tech/v1alpha1" ,
782+ "controller" : true ,
783+ "kind" : "OpenSearchCluster" ,
784+ "name" : "my-opensearch-cluster" ,
785+ "uid" : "0b1e30e6-326e-4c1a-868d-ad6598b49e8b" ,
786+ } ,
787+ ] ,
788+ } ,
789+ "data" : {
790+ "action_groups.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" actiongroups\" }}" ,
791+ "allowlist.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" allowlist\" },\" config\" :{\" enabled\" :false}}" ,
792+ "audit.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" audit\" },\" config\" :{\" enabled\" :false}}" ,
793+ "config.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" config\" },\" config\" :{\" dynamic\" :{\" authc\" :{},\" authz\" :{},\" http\" :{}}}}" ,
794+ "internal_users.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" internalusers\" }}" ,
795+ "nodes_dn.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" nodesdn\" }}" ,
796+ "roles.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" roles\" }}" ,
797+ "roles_mapping.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" rolesmapping\" }}" ,
798+ "tenants.yml" : "{\" _meta\" :{\" config_version\" :2,\" type\" :\" tenants\" }}" ,
799+ } ,
800+ } ) ,
801+ security_config_map
802+ ) ;
803+ }
804+
686805 #[ test]
687806 fn test_build_pdb ( ) {
688807 let context_names = context_names ( ) ;
0 commit comments