Skip to content

Commit bcd1480

Browse files
feat: Add annotations to the StatefulSet to ignore resources with security settings
1 parent a7dfe3c commit bcd1480

5 files changed

Lines changed: 101 additions & 60 deletions

File tree

rust/operator-binary/src/controller/build/role_builder.rs

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use stackable_operator::{
1313
rbac::v1::{ClusterRole, RoleBinding, RoleRef, Subject},
1414
},
1515
},
16+
kube::api::ObjectMeta,
1617
kvp::{
1718
Label, Labels,
1819
consts::{STACKABLE_VENDOR_KEY, STACKABLE_VENDOR_VALUE},
@@ -28,8 +29,7 @@ use crate::{
2829
framework::{
2930
NameIsValidLabelValue,
3031
builder::{
31-
meta::{annotation_ignore_restarter, ownerreference_from_resource},
32-
pdb::pod_disruption_budget_builder_with_role,
32+
meta::ownerreference_from_resource, pdb::pod_disruption_budget_builder_with_role,
3333
},
3434
role_utils::ResourceNames,
3535
types::{
@@ -81,9 +81,7 @@ impl<'a> RoleBuilder<'a> {
8181

8282
/// Builds a ServiceAccount used by all role-groups
8383
pub fn build_service_account(&self) -> ServiceAccount {
84-
let metadata = self
85-
.common_metadata(self.resource_names.service_account_name())
86-
.build();
84+
let metadata = self.common_metadata(self.resource_names.service_account_name());
8785

8886
ServiceAccount {
8987
metadata,
@@ -93,9 +91,7 @@ impl<'a> RoleBuilder<'a> {
9391

9492
/// Builds a RoleBinding used by all role-groups
9593
pub fn build_role_binding(&self) -> RoleBinding {
96-
let metadata = self
97-
.common_metadata(self.resource_names.role_binding_name())
98-
.build();
94+
let metadata = self.common_metadata(self.resource_names.role_binding_name());
9995

10096
RoleBinding {
10197
metadata,
@@ -121,9 +117,7 @@ impl<'a> RoleBuilder<'a> {
121117
..ServicePort::default()
122118
}];
123119

124-
let metadata = self
125-
.common_metadata(seed_nodes_service_name(&self.cluster.name))
126-
.build();
120+
let metadata = self.common_metadata(seed_nodes_service_name(&self.cluster.name));
127121

128122
let service_selector =
129123
RoleGroupBuilder::cluster_manager_labels(&self.cluster, self.context_names);
@@ -147,9 +141,7 @@ impl<'a> RoleBuilder<'a> {
147141

148142
/// Builds a Listener whose status is used to populate the discovery ConfigMap.
149143
pub fn build_discovery_service_listener(&self) -> listener::v1alpha1::Listener {
150-
let metadata = self
151-
.common_metadata(discovery_service_listener_name(&self.cluster.name))
152-
.build();
144+
let metadata = self.common_metadata(discovery_service_listener_name(&self.cluster.name));
153145

154146
let listener_class = &self.cluster.role_config.discovery_service_listener_class;
155147

@@ -178,9 +170,7 @@ impl<'a> RoleBuilder<'a> {
178170
pub fn build_maybe_discovery_config_map(&self) -> Option<ConfigMap> {
179171
let discovery_endpoint = self.cluster.discovery_endpoint.as_ref()?;
180172

181-
let metadata = self
182-
.common_metadata(discovery_config_map_name(&self.cluster.name))
183-
.build();
173+
let metadata = self.common_metadata(discovery_config_map_name(&self.cluster.name));
184174

185175
let protocol = if self.cluster.is_server_tls_enabled() {
186176
"https"
@@ -221,10 +211,7 @@ impl<'a> RoleBuilder<'a> {
221211
/// Returns `None` if the security plugin is disabled or all configuration files are
222212
/// references.
223213
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();
214+
let metadata = self.common_metadata(security_config_map_name(&self.cluster.name));
228215

229216
let mut data = BTreeMap::new();
230217

@@ -277,20 +264,17 @@ impl<'a> RoleBuilder<'a> {
277264
}
278265

279266
/// Common metadata for role resources
280-
fn common_metadata(&self, resource_name: impl Into<String>) -> ObjectMetaBuilder {
281-
let mut builder = ObjectMetaBuilder::new();
282-
283-
builder
267+
fn common_metadata(&self, resource_name: impl Into<String>) -> ObjectMeta {
268+
ObjectMetaBuilder::new()
284269
.name(resource_name)
285270
.namespace(&self.cluster.namespace)
286271
.ownerreference(ownerreference_from_resource(
287272
&self.cluster,
288273
None,
289274
Some(true),
290275
))
291-
.with_labels(self.labels());
292-
293-
builder
276+
.with_labels(self.labels())
277+
.build()
294278
}
295279

296280
/// Common labels for role resources
@@ -763,9 +747,6 @@ mod tests {
763747
"apiVersion": "v1",
764748
"kind": "ConfigMap",
765749
"metadata": {
766-
"annotations": {
767-
"restarter.stackable.tech/ignore": "true"
768-
},
769750
"labels": {
770751
"app.kubernetes.io/component": "nodes",
771752
"app.kubernetes.io/instance": "my-opensearch-cluster",

rust/operator-binary/src/controller/build/role_group_builder.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Builder for role group resources
22
3-
use std::{collections::BTreeMap, str::FromStr};
3+
use std::{
4+
collections::{BTreeMap, BTreeSet},
5+
str::FromStr,
6+
};
47

58
use stackable_operator::{
69
builder::{
@@ -70,8 +73,8 @@ use crate::{
7073
role_group_utils::ResourceNames,
7174
types::{
7275
kubernetes::{
73-
ListenerName, PersistentVolumeClaimName, SecretClassName, ServiceAccountName,
74-
ServiceName, VolumeName,
76+
ConfigMapName, ListenerName, PersistentVolumeClaimName, SecretClassName,
77+
SecretName, ServiceAccountName, ServiceName, VolumeName,
7578
},
7679
operator::RoleGroupName,
7780
},
@@ -309,6 +312,7 @@ impl<'a> RoleGroupBuilder<'a> {
309312
let metadata = self
310313
.common_metadata(self.resource_names.stateful_set_name())
311314
.with_label(RESTART_CONTROLLER_ENABLED_LABEL.to_owned())
315+
.with_annotations(self.restarter_ignore_annotations())
312316
.build();
313317

314318
let template = self.build_pod_template();
@@ -373,6 +377,42 @@ impl<'a> RoleGroupBuilder<'a> {
373377
}
374378
}
375379

380+
fn restarter_ignore_annotations(&self) -> Annotations {
381+
let (security_settings_config_maps, security_settings_secrets) =
382+
self.security_settings_resource_names();
383+
384+
let restarter_ignore_config_maps_annotations = security_settings_config_maps
385+
.iter()
386+
.enumerate()
387+
.map(|(i, config_map_name)| {
388+
(
389+
format!("restarter.stackable.tech/ignore-config-map.{i}"),
390+
config_map_name.to_string(),
391+
)
392+
});
393+
let restarter_ignore_secrets_annotations = security_settings_secrets
394+
.iter()
395+
.enumerate()
396+
.map(|(i, secret_name)| {
397+
(
398+
format!("restarter.stackable.tech/ignore-secret.{i}"),
399+
secret_name.to_string(),
400+
)
401+
});
402+
403+
// The expectation is tested in the unit tests.
404+
Annotations::try_from(
405+
restarter_ignore_config_maps_annotations
406+
.chain(restarter_ignore_secrets_annotations)
407+
.collect::<BTreeMap<_, _>>(),
408+
)
409+
.expect(
410+
"should contain only valid annotations because the annotation keys are statically \
411+
defined apart from the index number and the names of ConfigMaps and Secrets are valid \
412+
annotation values.",
413+
)
414+
}
415+
376416
/// Builds the [`PodTemplateSpec`] for the [`StatefulSet`] of the role group
377417
fn build_pod_template(&self) -> PodTemplateSpec {
378418
let mut node_role_labels = Labels::new();
@@ -1123,6 +1163,39 @@ impl<'a> RoleGroupBuilder<'a> {
11231163
}]
11241164
}
11251165

1166+
fn security_settings_resource_names(&self) -> (BTreeSet<ConfigMapName>, BTreeSet<SecretName>) {
1167+
let mut config_map_names = BTreeSet::new();
1168+
let mut secret_names = BTreeSet::new();
1169+
1170+
if let RoleGroupSecurityMode::Initializing { settings, .. }
1171+
| RoleGroupSecurityMode::Managing { settings, .. } = &self.security_mode
1172+
{
1173+
for file_type in settings {
1174+
match &file_type.content {
1175+
v1alpha1::SecuritySettingsFileTypeContent::Value(_) => {
1176+
config_map_names.insert(security_config_map_name(&self.cluster.name));
1177+
}
1178+
v1alpha1::SecuritySettingsFileTypeContent::ValueFrom(
1179+
v1alpha1::SecuritySettingsFileTypeContentValueFrom::ConfigMapKeyRef(
1180+
v1alpha1::ConfigMapKeyRef { name, .. },
1181+
),
1182+
) => {
1183+
config_map_names.insert(name.clone());
1184+
}
1185+
v1alpha1::SecuritySettingsFileTypeContent::ValueFrom(
1186+
v1alpha1::SecuritySettingsFileTypeContentValueFrom::SecretKeyRef(
1187+
v1alpha1::SecretKeyRef { name, .. },
1188+
),
1189+
) => {
1190+
secret_names.insert(name.clone());
1191+
}
1192+
};
1193+
}
1194+
}
1195+
1196+
(config_map_names, secret_names)
1197+
}
1198+
11261199
/// Builds the security settings volumes for the [`PodTemplateSpec`]
11271200
/// It is not checked if these volumes are required in this role group.
11281201
fn build_security_settings_volumes(
@@ -1712,6 +1785,15 @@ mod tests {
17121785
let stateful_set = serde_json::to_value(role_group_builder.build_stateful_set())
17131786
.expect("should be serializable");
17141787

1788+
let expected_annotations = match security_mode {
1789+
TestSecurityMode::Initializing | TestSecurityMode::Managing => json!({
1790+
"restarter.stackable.tech/ignore-config-map.0": "my-opensearch-cluster-security-config",
1791+
"restarter.stackable.tech/ignore-config-map.1": "opensearch-security-config",
1792+
"restarter.stackable.tech/ignore-secret.0": "opensearch-security-config",
1793+
}),
1794+
TestSecurityMode::Disabled | TestSecurityMode::Participating => json!({}),
1795+
};
1796+
17151797
let expected_opensearch_container_volume_mounts = match security_mode {
17161798
TestSecurityMode::Initializing => json!([
17171799
{
@@ -3026,6 +3108,7 @@ mod tests {
30263108
"apiVersion": "apps/v1",
30273109
"kind": "StatefulSet",
30283110
"metadata": {
3111+
"annotations": expected_annotations,
30293112
"labels": {
30303113
"app.kubernetes.io/component": "nodes",
30313114
"app.kubernetes.io/instance": "my-opensearch-cluster",

rust/operator-binary/src/framework/builder/meta.rs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use stackable_operator::{
22
builder::meta::OwnerReferenceBuilder,
33
k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference, kube::Resource,
4-
kvp::Annotation,
54
};
65

76
use crate::framework::{HasName, HasUid};
@@ -29,15 +28,6 @@ pub fn ownerreference_from_resource(
2928
)
3029
}
3130

32-
/// Annotation which signals the restarter to ignore this resource.
33-
pub fn annotation_ignore_restarter() -> Annotation {
34-
Annotation::try_from((
35-
"restarter.stackable.tech/ignore".to_owned(),
36-
"true".to_owned(),
37-
))
38-
.expect("should be a valid annotation")
39-
}
40-
4131
#[cfg(test)]
4232
mod tests {
4333
use std::borrow::Cow;
@@ -47,10 +37,7 @@ mod tests {
4737
kube::Resource,
4838
};
4939

50-
use crate::framework::{
51-
HasName, HasUid, Uid,
52-
builder::meta::{annotation_ignore_restarter, ownerreference_from_resource},
53-
};
40+
use crate::framework::{HasName, HasUid, Uid, builder::meta::ownerreference_from_resource};
5441

5542
struct Cluster {
5643
object_meta: ObjectMeta,
@@ -134,10 +121,4 @@ mod tests {
134121

135122
assert_eq!(expected_owner_reference, actual_owner_reference);
136123
}
137-
138-
#[test]
139-
fn test_annotation_ignore_restarter() {
140-
// Test that the function does not panic
141-
annotation_ignore_restarter();
142-
}
143124
}

tests/templates/kuttl/security-config/10-security-config.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ apiVersion: v1
33
kind: Secret
44
metadata:
55
name: security-config-file-internal-users
6-
annotations:
7-
restarter.stackable.tech/ignore: "true"
86
stringData:
97
internal_users.yml: |
108
---
@@ -23,8 +21,6 @@ apiVersion: v1
2321
kind: ConfigMap
2422
metadata:
2523
name: security-config
26-
annotations:
27-
restarter.stackable.tech/ignore: "true"
2824
data:
2925
roles.yml: |
3026
---

tests/templates/kuttl/security-config/11-assert.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ apiVersion: apps/v1
2323
kind: StatefulSet
2424
metadata:
2525
name: opensearch-nodes-security-config
26+
annotations:
27+
restarter.stackable.tech/ignore-config-map.0: "opensearch-security-config"
2628
status:
2729
readyReplicas: 1
2830
replicas: 1
@@ -31,8 +33,6 @@ apiVersion: v1
3133
kind: ConfigMap
3234
metadata:
3335
name: opensearch-security-config
34-
annotations:
35-
restarter.stackable.tech/ignore: "true"
3636
data:
3737
action_groups.yml: '{"_meta":{"config_version":2,"type":"actiongroups"}}'
3838
allowlist.yml: '{"_meta":{"config_version":2,"type":"allowlist"},"config":{"enabled":false}}'

0 commit comments

Comments
 (0)