Skip to content

Commit 696023c

Browse files
soenkeliebaunightkrsiegfriedweber
authored
Fixes accidentally deleted serviceaccount and rolebinding objects (#909)
* Potential fix for SUP-148 * Expanded comment on build_rbac_resources a bit. * fix rustdocs complaints * Adapt tests to the changed behavior. * Changed role_binding_name and service_account_name to not be public anymore. Operators should not call these with potentially wrong parameters, but instead use `build_rbac_resources` to retrieve the objects and read the name from there. * Add legacy name of serviceAccount to roleBinding as well for better transition period. * Added changelog entry. * Update crates/stackable-operator/src/commons/rbac.rs Co-authored-by: Natalie Klestrup Röijezon <nat.roijezon@stackable.tech> * Fix changelog --------- Co-authored-by: Natalie Klestrup Röijezon <nat.roijezon@stackable.tech> Co-authored-by: Siegfried Weber <mail@siegfriedweber.net>
1 parent bd6079e commit 696023c

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

crates/stackable-operator/CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@ All notable changes to this project will be documented in this file.
1111
### Changed
1212

1313
- BREAKING: Made `DEFAULT_OIDC_WELLKNOWN_PATH` private. Use `AuthenticationProvider::well_known_config_url` instead ([#910]).
14-
14+
- BREAKING: Changed visibility of `commons::rbac::service_account_name` and `commons::rbac::role_binding_name` to
15+
private, as these functions should not be called directly by the operators. This is likely to result in naming conflicts
16+
as the result is completely dependent on what is passed to this function. Operators should instead rely on the roleBinding
17+
and serviceAccount objects created by `commons::rbac::build_rbac_resources` and retrieve the name from the returned
18+
objects if they need it ([#909]).
19+
- Changed the names of the objects that are returned from `commons::rbac::build_rbac_resources` to not rely solely on the product
20+
they refer to (e.g. "nifi-rolebinding") but instead include the name of the resource to be unique per cluster
21+
(e.g. simple-nifi-rolebinding) ([#909]).
22+
23+
[#909]: https://github.com/stackabletech/operator-rs/pull/909
1524
[#910]: https://github.com/stackabletech/operator-rs/pull/910
1625

1726
## [0.81.0] - 2024-11-05

crates/stackable-operator/src/commons/rbac.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,25 @@ pub enum Error {
2828
}
2929

3030
/// Build RBAC objects for the product workloads.
31-
/// The `rbac_prefix` is meant to be the product name, for example: zookeeper, airflow, etc.
32-
/// and it is a assumed that a ClusterRole named `{rbac_prefix}-clusterrole` exists.
31+
/// The `product_name` is meant to be the product name, for example: zookeeper, airflow, etc.
32+
/// and it is a assumed that a ClusterRole named `{product_name}-clusterrole` exists.
33+
3334
pub fn build_rbac_resources<T: Clone + Resource<DynamicType = ()>>(
3435
resource: &T,
35-
rbac_prefix: &str,
36+
// 'product_name' is not used to build the names of the serviceAccount and roleBinding objects,
37+
// as this caused problems with multiple clusters of the same product within the same namespace
38+
// see <https://stackable.atlassian.net/browse/SUP-148> for more details.
39+
// Instead the names for these objects are created by reading the name from the cluster object
40+
// and appending [-rolebinding|-serviceaccount] to create unique names instead of using the
41+
// same objects for multiple clusters.
42+
product_name: &str,
3643
labels: Labels,
3744
) -> Result<(ServiceAccount, RoleBinding)> {
38-
let sa_name = service_account_name(rbac_prefix);
45+
let sa_name = service_account_name(&resource.name_any());
46+
// We add the legacy serviceAccount name to the binding here for at least one
47+
// release cycle, so that the switchover during the upgrade can be smoother.
48+
// To be removed in v24.3+1.
49+
let legacy_sa_name = service_account_name(product_name);
3950
let service_account = ServiceAccount {
4051
metadata: ObjectMetaBuilder::new()
4152
.name_and_namespace(resource)
@@ -52,7 +63,7 @@ pub fn build_rbac_resources<T: Clone + Resource<DynamicType = ()>>(
5263
let role_binding = RoleBinding {
5364
metadata: ObjectMetaBuilder::new()
5465
.name_and_namespace(resource)
55-
.name(role_binding_name(rbac_prefix))
66+
.name(role_binding_name(&resource.name_any()))
5667
.ownerreference_from_resource(resource, None, Some(true))
5768
.context(RoleBindingOwnerReferenceFromResourceSnafu {
5869
name: resource.name_any(),
@@ -61,29 +72,45 @@ pub fn build_rbac_resources<T: Clone + Resource<DynamicType = ()>>(
6172
.build(),
6273
role_ref: RoleRef {
6374
kind: "ClusterRole".to_string(),
64-
name: format!("{rbac_prefix}-clusterrole"),
75+
name: format!("{product_name}-clusterrole"),
6576
api_group: "rbac.authorization.k8s.io".to_string(),
6677
},
67-
subjects: Some(vec![Subject {
68-
kind: "ServiceAccount".to_string(),
69-
name: sa_name,
70-
namespace: resource.namespace(),
71-
..Subject::default()
72-
}]),
78+
subjects: Some(vec![
79+
Subject {
80+
kind: "ServiceAccount".to_string(),
81+
name: sa_name,
82+
namespace: resource.namespace(),
83+
..Subject::default()
84+
},
85+
// We add the legacy serviceAccount name to the binding here for at least one
86+
// release cycle, so that the switchover during the upgrade can be smoother.
87+
Subject {
88+
kind: "ServiceAccount".to_string(),
89+
name: legacy_sa_name,
90+
namespace: resource.namespace(),
91+
..Subject::default()
92+
},
93+
]),
7394
};
7495

7596
Ok((service_account, role_binding))
7697
}
7798

7899
/// Generate the service account name.
79100
/// The `rbac_prefix` is meant to be the product name, for example: zookeeper, airflow, etc.
80-
pub fn service_account_name(rbac_prefix: &str) -> String {
101+
/// This is private because operators should not use this function to calculate names for
102+
/// serviceAccount objects, but rather read the name from the objects returned by
103+
/// `build_rbac_resources` if they need the name.
104+
fn service_account_name(rbac_prefix: &str) -> String {
81105
format!("{rbac_prefix}-serviceaccount")
82106
}
83107

84108
/// Generate the role binding name.
85109
/// The `rbac_prefix` is meant to be the product name, for example: zookeeper, airflow, etc.
86-
pub fn role_binding_name(rbac_prefix: &str) -> String {
110+
/// This is private because operators should not use this function to calculate names for
111+
/// roleBinding objects, but rather read the name from the objects returned by
112+
/// `build_rbac_resources` if they need the name.
113+
fn role_binding_name(rbac_prefix: &str) -> String {
87114
format!("{rbac_prefix}-rolebinding")
88115
}
89116

@@ -130,7 +157,7 @@ mod tests {
130157
build_rbac_resources(&cluster, RESOURCE_NAME, Labels::new()).unwrap();
131158

132159
assert_eq!(
133-
Some(service_account_name(RESOURCE_NAME)),
160+
Some(service_account_name(CLUSTER_NAME)),
134161
rbac_sa.metadata.name,
135162
"service account does not match"
136163
);
@@ -141,7 +168,7 @@ mod tests {
141168
);
142169

143170
assert_eq!(
144-
Some(role_binding_name(RESOURCE_NAME)),
171+
Some(role_binding_name(CLUSTER_NAME)),
145172
rbac_rolebinding.metadata.name,
146173
"rolebinding does not match"
147174
);

0 commit comments

Comments
 (0)