Skip to content

Commit 5184eb3

Browse files
authored
feat(operator): Add LabelExt trait (#1106)
* feat(operator): Add LabelExt trait This trait enables adding one or multiple labels to any Kubernetes resource (through kube's ResourceExt trait). * feat(operator): Add new convenience functions to Label This adds the following new associated functions to construct labels: - `Label::instance`: app.kubernetes.io/instance - `Label::name`: app.kubernetes.io/name - `Label::stackable_vendor`: stackable.tech/vendor=Stackable * chore(operator): Fix and adjust doc comments * chore(operator): Add changelog entry * chore: Fix changelogs
1 parent 5b7a4f6 commit 5184eb3

File tree

3 files changed

+118
-22
lines changed

3 files changed

+118
-22
lines changed

crates/stackable-operator/CHANGELOG.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ All notable changes to this project will be documented in this file.
66

77
### Added
88

9+
- Add `LabelExt` trait which enables adding validated labels to any Kubernetes resource ([#1106]).
10+
- Add new associated convenience functions to `Label` ([#1106]).
11+
- `Label::stackable_vendor`: stackable.tech/vendor=Stackable
12+
- `Label::instance`: app.kubernetes.io/instance
13+
- `Label::name`: app.kubernetes.io/name
14+
- Add a `Client::create_if_missing` associated function to create a resource if it doesn't
15+
exist ([#1099]).
916
- BREAKING: Add new ListenerClass `.spec.pinnedNodePorts` field ([#1105]).
1017

18+
[#1099]: https://github.com/stackabletech/operator-rs/pull/1099
1119
[#1105]: https://github.com/stackabletech/operator-rs/pull/1105
20+
[#1106]: https://github.com/stackabletech/operator-rs/pull/1106
1221

1322
## [0.99.0] - 2025-10-06
1423

1524
### Added
1625

17-
- Add `CustomResourceDefinitionMaintainer` which applies and patches CRDs triggered by TLS
18-
certificate rotations of the `ConversionWebhookServer`. It additionally provides a `oneshot`
19-
channel which can for example be used to trigger creation/patching of any custom resources deployed by
20-
the operator ([#1099]).
21-
- Add a `Client::create_if_missing` associated function to create a resource if it doesn't
22-
exist ([#1099]).
2326
- Add CLI argument and env var to disable the end-of-support checker: `EOS_DISABLED` (`--eos-disabled`) ([#1101]).
2427
- Add end-of-support checker ([#1096], [#1103]).
2528
- The EoS checker can be constructed using `EndOfSupportChecker::new()`.
@@ -40,7 +43,6 @@ All notable changes to this project will be documented in this file.
4043

4144
[#1096]: https://github.com/stackabletech/operator-rs/pull/1096
4245
[#1098]: https://github.com/stackabletech/operator-rs/pull/1098
43-
[#1099]: https://github.com/stackabletech/operator-rs/pull/1099
4446
[#1101]: https://github.com/stackabletech/operator-rs/pull/1101
4547
[#1103]: https://github.com/stackabletech/operator-rs/pull/1103
4648

crates/stackable-operator/src/kvp/label/mod.rs

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! This module provides various types and functions to construct valid
22
//! Kubernetes labels. Labels are key/value pairs, where the key must meet
3-
//! certain requirementens regarding length and character set. The value can
3+
//! certain requirements regarding length and character set. The value can
44
//! contain a limited set of ASCII characters.
55
//!
66
//! Additionally, the [`Label`] struct provides various helper functions to
@@ -42,6 +42,63 @@ pub type LabelsError = KeyValuePairsError;
4242
/// of labels fails.
4343
pub type LabelError = KeyValuePairError<LabelValueError>;
4444

45+
/// Add [`Label`]s to any Kubernetes resource.
46+
///
47+
/// It should be noted, that after the addition of labels to the resource, the validity of keys and
48+
/// values **can no longer be enforced** as they are both stored as plain [`String`]s. To update a
49+
/// label use [`LabelExt::add_label`] which will update the label in place if it is already present.
50+
pub trait LabelExt
51+
where
52+
Self: ResourceExt,
53+
{
54+
/// Adds a single label to `self`.
55+
fn add_label(&mut self, label: Label) -> &mut Self;
56+
57+
/// Adds multiple labels to `self`.
58+
fn add_labels(&mut self, label: Labels) -> &mut Self;
59+
}
60+
61+
impl<T> LabelExt for T
62+
where
63+
T: ResourceExt,
64+
{
65+
fn add_label(&mut self, label: Label) -> &mut Self {
66+
let meta = self.meta_mut();
67+
68+
match &mut meta.labels {
69+
Some(labels) => {
70+
// TODO (@Techassi): Add an API to consume key and value
71+
let KeyValuePair { key, value } = label.into_inner();
72+
labels.insert(key.to_string(), value.to_string());
73+
}
74+
None => {
75+
let mut labels = BTreeMap::new();
76+
77+
// TODO (@Techassi): Add an API to consume key and value
78+
let KeyValuePair { key, value } = label.into_inner();
79+
labels.insert(key.to_string(), value.to_string());
80+
81+
meta.labels = Some(labels);
82+
}
83+
}
84+
85+
self
86+
}
87+
88+
fn add_labels(&mut self, labels: Labels) -> &mut Self {
89+
let meta = self.meta_mut();
90+
91+
match &mut meta.labels {
92+
Some(existing_labels) => {
93+
existing_labels.extend::<BTreeMap<String, String>>(labels.into())
94+
}
95+
None => meta.labels = Some(labels.into()),
96+
}
97+
98+
self
99+
}
100+
}
101+
45102
/// A specialized implementation of a key/value pair representing Kubernetes
46103
/// labels.
47104
///
@@ -99,26 +156,28 @@ impl Label {
99156
self.0
100157
}
101158

102-
/// Creates the `app.kubernetes.io/component` label with `role` as the
103-
/// value. This function will return an error if `role` violates the required
104-
/// Kubernetes restrictions.
159+
/// Creates the `app.kubernetes.io/component` label with `role` as the value.
160+
///
161+
/// This function will return an error if `role` violates the required Kubernetes restrictions.
105162
pub fn component(component: &str) -> Result<Self, LabelError> {
106163
let kvp = KeyValuePair::try_from((K8S_APP_COMPONENT_KEY, component))?;
107164
Ok(Self(kvp))
108165
}
109166

110-
/// Creates the `app.kubernetes.io/role-group` label with `role_group` as
111-
/// the value. This function will return an error if `role_group` violates
112-
/// the required Kubernetes restrictions.
167+
/// Creates the `app.kubernetes.io/role-group` label with `role_group` as the value.
168+
///
169+
/// This function will return an error if `role_group` violates the required Kubernetes
170+
/// restrictions.
113171
pub fn role_group(role_group: &str) -> Result<Self, LabelError> {
114172
let kvp = KeyValuePair::try_from((K8S_APP_ROLE_GROUP_KEY, role_group))?;
115173
Ok(Self(kvp))
116174
}
117175

118-
/// Creates the `app.kubernetes.io/managed-by` label with the formated
119-
/// full controller name based on `operator_name` and `controller_name` as
120-
/// the value. This function will return an error if the formatted controller
121-
/// name violates the required Kubernetes restrictions.
176+
/// Creates the `app.kubernetes.io/managed-by` label with the formatted full controller name
177+
/// based on `operator_name` and `controller_name` as the value.
178+
///
179+
/// This function will return an error if the formatted controller name violates the required
180+
/// Kubernetes restrictions.
122181
pub fn managed_by(operator_name: &str, controller_name: &str) -> Result<Self, LabelError> {
123182
let kvp = KeyValuePair::try_from((
124183
K8S_APP_MANAGED_BY_KEY,
@@ -127,14 +186,40 @@ impl Label {
127186
Ok(Self(kvp))
128187
}
129188

130-
/// Creates the `app.kubernetes.io/version` label with `version` as the
131-
/// value. This function will return an error if `role_group` violates the
132-
/// required Kubernetes restrictions.
189+
/// Creates the `app.kubernetes.io/version` label with `version` as the value.
190+
///
191+
/// This function will return an error if `version` violates the required Kubernetes
192+
/// restrictions.
133193
pub fn version(version: &str) -> Result<Self, LabelError> {
134194
// NOTE (Techassi): Maybe use semver::Version
135195
let kvp = KeyValuePair::try_from((K8S_APP_VERSION_KEY, version))?;
136196
Ok(Self(kvp))
137197
}
198+
199+
/// Creates the `app.kubernetes.io/instance` label with `instance` as the value.
200+
///
201+
/// This function will return an error if `instance` violates the required Kubernetes
202+
/// restrictions.
203+
pub fn instance(instance: &str) -> Result<Self, LabelError> {
204+
let kvp = KeyValuePair::try_from((K8S_APP_INSTANCE_KEY, instance))?;
205+
Ok(Self(kvp))
206+
}
207+
208+
/// Creates the `app.kubernetes.io/name` label with `name` as the value.
209+
///
210+
/// This function will return an error if `name` violates the required Kubernetes restrictions.
211+
pub fn name(name: &str) -> Result<Self, LabelError> {
212+
let kvp = KeyValuePair::try_from((K8S_APP_NAME_KEY, name))?;
213+
Ok(Self(kvp))
214+
}
215+
216+
/// Creates the Stackable specific vendor label.
217+
///
218+
/// See [`STACKABLE_VENDOR_KEY`] and [`STACKABLE_VENDOR_VALUE`].
219+
pub fn stackable_vendor() -> Self {
220+
Self::try_from((STACKABLE_VENDOR_KEY, STACKABLE_VENDOR_VALUE))
221+
.expect("constant vendor label must be valid")
222+
}
138223
}
139224

140225
/// A validated set/list of Kubernetes labels.
@@ -331,7 +416,7 @@ impl Labels {
331416
labels.insert(version);
332417

333418
// Stackable-specific labels
334-
labels.parse_insert((STACKABLE_VENDOR_KEY, STACKABLE_VENDOR_VALUE))?;
419+
labels.insert(Label::stackable_vendor());
335420

336421
Ok(labels)
337422
}

crates/stackable-webhook/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Add `CustomResourceDefinitionMaintainer` which applies and patches CRDs triggered by TLS
10+
certificate rotations of the `ConversionWebhookServer`. It additionally provides a `oneshot`
11+
channel which can for example be used to trigger creation/patching of any custom resources
12+
deployed by the operator ([#1099]).
13+
- Add `ConversionWebhookServer::with_maintainer` which creates a conversion webhook server and a CRD
14+
maintainer ([#1099]).
15+
716
### Changed
817

918
- BREAKING: `ConversionWebhookServer::new` now returns a pair of values ([#1099]):

0 commit comments

Comments
 (0)