33use std:: {
44 collections:: { BTreeMap , HashSet } ,
55 marker:: PhantomData ,
6+ str:: FromStr ,
67} ;
78
89use snafu:: { ResultExt , Snafu } ;
@@ -19,7 +20,7 @@ use stackable_operator::{
1920 kvp:: label,
2021 types:: {
2122 kubernetes:: SecretKey ,
22- operator:: { RoleGroupName , RoleName } ,
23+ operator:: { ProductVersion , RoleGroupName , RoleName } ,
2324 } ,
2425 } ,
2526} ;
@@ -55,6 +56,10 @@ pub mod resource;
5556pub mod role_group;
5657pub mod security;
5758
59+ // Placeholder value for the `app.kubernetes.io/version` label on resources whose labels are
60+ // immutable once deployed. See `recommended_labels_for_unversioned_role_group_resources`.
61+ stackable_operator:: constant!( UNVERSIONED_PRODUCT_VERSION : ProductVersion = "none" ) ;
62+
5863/// Returns an [`ObjectMetaBuilder`] pre-filled with the cluster's namespace, an owner
5964/// reference back to the cluster, the resource `name` and the given `recommended_labels`.
6065///
@@ -114,14 +119,25 @@ pub(crate) fn recommended_labels_for_role_group_resources(
114119 )
115120}
116121
122+ /// The recommended labels for role-group resources that carry no product version.
123+ ///
124+ /// The `app.kubernetes.io/version` label is set to the placeholder `none` rather than being
125+ /// omitted, because that is what operator versions up to 26.7 wrote. Omitting it would change the
126+ /// `volumeClaimTemplates` of already deployed StatefulSets, and since those templates are
127+ /// immutable, Kubernetes rejects the update and the operator can no longer reconcile the
128+ /// StatefulSet.
129+ ///
130+ /// This is also why `label::recommended_labels_for_unversioned_role_group_resources`, which drops
131+ /// the label entirely, is not used here.
117132pub ( crate ) fn recommended_labels_for_unversioned_role_group_resources (
118133 cluster : & ValidatedCluster ,
119134 role_name : & RoleName ,
120135 role_group_name : & RoleGroupName ,
121136) -> Labels {
122- label:: recommended_labels_for_unversioned_role_group_resources (
137+ label:: recommended_labels_for_role_group_resources (
123138 & cluster. name ,
124139 & PRODUCT_NAME ,
140+ & UNVERSIONED_PRODUCT_VERSION ,
125141 & OPERATOR_NAME ,
126142 & CONTROLLER_NAME ,
127143 role_name,
@@ -483,7 +499,7 @@ mod tests {
483499 use strum:: IntoEnumIterator ;
484500
485501 use super :: {
486- KubernetesResources , Prepared , SecretKey , build,
502+ KubernetesResources , Prepared , SecretKey , UNVERSIONED_PRODUCT_VERSION , build,
487503 container:: {
488504 DRUID_CONFIG_VOLUME_NAME , HDFS_CONFIG_VOLUME_NAME , LOG_CONFIG_VOLUME_NAME ,
489505 LOG_VOLUME_NAME , RW_CONFIG_VOLUME_NAME ,
@@ -752,6 +768,12 @@ spec:
752768 . expect ( "the ConfigMap has data" )
753769 }
754770
771+ #[ test]
772+ fn test_constants ( ) {
773+ // Test that dereferencing the constant does not panic.
774+ let _ = * UNVERSIONED_PRODUCT_VERSION ;
775+ }
776+
755777 /// A pod's `volumes` are an ordered list: reordering them changes the pod template of every
756778 /// running StatefulSet and so restarts its pods. Pinned on the fixture that has every
757779 /// optional volume: the operator-managed volumes first, the Historical segment cache after
0 commit comments