From 02a78b382305db40cecf0248a3394acb75a69bb5 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 13 Aug 2026 13:59:54 +0200 Subject: [PATCH 1/6] remove version from pvc listener labels, correct operator name --- .../src/controller/build/mod.rs | 2 +- .../controller/build/resource/statefulset.rs | 6 ++- rust/operator-binary/src/crd/mod.rs | 2 +- tests/templates/kuttl/smoke/30-assert.yaml.j2 | 48 +++++++++---------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/rust/operator-binary/src/controller/build/mod.rs b/rust/operator-binary/src/controller/build/mod.rs index aa735900..ad76660b 100644 --- a/rust/operator-binary/src/controller/build/mod.rs +++ b/rust/operator-binary/src/controller/build/mod.rs @@ -245,7 +245,7 @@ mod tests { ("app.kubernetes.io/instance", "my-hbase".to_string()), ( "app.kubernetes.io/managed-by", - "hbase.stackable.com_hbasecluster".to_string(), + "hbase.stackable.tech_hbasecluster".to_string(), ), ("app.kubernetes.io/name", "hbase".to_string()), ("app.kubernetes.io/role-group", "none".to_string()), diff --git a/rust/operator-binary/src/controller/build/resource/statefulset.rs b/rust/operator-binary/src/controller/build/resource/statefulset.rs index ce189e7a..16068353 100644 --- a/rust/operator-binary/src/controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/controller/build/resource/statefulset.rs @@ -299,8 +299,12 @@ pub fn build_rolegroup_statefulset( )); } + // Listener PVC labels should stay stable across upgrades and so should not + // include the version field (see HDFS for a similar pattern). + let unversioned_labels = cluster.role_group_selector(hbase_role, role_group_name); + let listener_pvc = - super::listener::build_listener_pvc(hbase_role, merged_config, &recommended_labels); + super::listener::build_listener_pvc(hbase_role, merged_config, &unversioned_labels); if let Some(listener_volume) = super::listener::build_listener_volume(hbase_role, merged_config, &recommended_labels) diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index b398c860..0535b077 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -44,7 +44,7 @@ pub mod security; pub const APP_NAME: &str = "hbase"; pub const FIELD_MANAGER: &str = "hbase-operator"; -pub const OPERATOR_NAME: &str = "hbase.stackable.com"; +pub const OPERATOR_NAME: &str = "hbase.stackable.tech"; // This constant is hard coded in hbase-entrypoint.sh // You need to change it there too. diff --git a/tests/templates/kuttl/smoke/30-assert.yaml.j2 b/tests/templates/kuttl/smoke/30-assert.yaml.j2 index 067fc721..120e46db 100644 --- a/tests/templates/kuttl/smoke/30-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-assert.yaml.j2 @@ -11,7 +11,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -46,7 +46,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -73,7 +73,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -108,7 +108,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -135,7 +135,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -170,7 +170,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -199,7 +199,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -224,7 +224,7 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -406,7 +406,7 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -430,7 +430,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -450,7 +450,7 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -641,7 +641,7 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -665,7 +665,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -690,7 +690,7 @@ spec: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -871,7 +871,7 @@ spec: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -893,7 +893,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: discovery stackable.tech/vendor: Stackable @@ -910,7 +910,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -927,7 +927,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -944,7 +944,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -960,7 +960,7 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-serviceaccount ownerReferences: @@ -974,7 +974,7 @@ kind: RoleBinding metadata: labels: app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-rolebinding ownerReferences: @@ -1023,7 +1023,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -1056,7 +1056,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -1089,7 +1089,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.com_hbasecluster + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable From de3a191bef74449e501755f8003b98f7fbb7aad0 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 13 Aug 2026 14:13:13 +0200 Subject: [PATCH 2/6] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f772b97a..f29153fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,12 +18,17 @@ - Fix a longstanding problem of including empty `categories`, `shortNames` and `additionalPrinterColumns` in the CRDs, which could cause problems with GitOps tools (e.g. ArgoCD) reporting a diff in the custom resources. See [our internal issue](https://github.com/stackabletech/hdfs-operator/issues/626) and [the fix](https://github.com/kube-rs/kube/pull/2042) for details ([#792]). +- BREAKING: The app.kubernetes.io/managed-by label value changed from hbase.stackable.com_hbasecluster to + hbase.stackable.tech_hbasecluster, aligning with all other operators. +- BREAKING: The rest-server listener PVC template now carries only the unversioned selector labels. + Existing rest-server StatefulSets must be deleted once before the new operator can reconcile them ([#795]). [#776]: https://github.com/stackabletech/hbase-operator/pull/776 [#782]: https://github.com/stackabletech/hbase-operator/pull/782 [#786]: https://github.com/stackabletech/hbase-operator/pull/786 [#787]: https://github.com/stackabletech/hbase-operator/pull/787 [#792]: https://github.com/stackabletech/hbase-operator/pull/792 +[#795]: https://github.com/stackabletech/hbase-operator/pull/795 ## [26.7.0] - 2026-07-21 From 6a5165d8cff020b891fcc986f8b34c72fea50979 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 13 Aug 2026 15:12:48 +0200 Subject: [PATCH 3/6] add vendor label manually, adjust test assert --- .../controller/build/resource/statefulset.rs | 5 +++- tests/templates/kuttl/smoke/30-assert.yaml.j2 | 24 ------------------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/rust/operator-binary/src/controller/build/resource/statefulset.rs b/rust/operator-binary/src/controller/build/resource/statefulset.rs index 16068353..86bef023 100644 --- a/rust/operator-binary/src/controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/controller/build/resource/statefulset.rs @@ -19,6 +19,7 @@ use stackable_operator::{ }, apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString}, }, + kvp::Label, product_logging, v2::{ builder::pod::container::{EnvVarName, EnvVarSet, new_container_builder}, @@ -301,7 +302,9 @@ pub fn build_rolegroup_statefulset( // Listener PVC labels should stay stable across upgrades and so should not // include the version field (see HDFS for a similar pattern). - let unversioned_labels = cluster.role_group_selector(hbase_role, role_group_name); + let mut unversioned_labels = cluster.role_group_selector(hbase_role, role_group_name); + // Vendor is not included in role_group_selector labels, so add it. + unversioned_labels.insert(Label::stackable_vendor()); let listener_pvc = super::listener::build_listener_pvc(hbase_role, merged_config, &unversioned_labels); diff --git a/tests/templates/kuttl/smoke/30-assert.yaml.j2 b/tests/templates/kuttl/smoke/30-assert.yaml.j2 index 120e46db..b2243107 100644 --- a/tests/templates/kuttl/smoke/30-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-assert.yaml.j2 @@ -11,7 +11,6 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -46,7 +45,6 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -73,7 +71,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -108,7 +105,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -135,7 +131,6 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -170,7 +165,6 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -199,7 +193,6 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -224,7 +217,6 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -406,7 +398,6 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -430,7 +421,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -450,7 +440,6 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -641,7 +630,6 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -665,7 +653,6 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -690,7 +677,6 @@ spec: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -871,7 +857,6 @@ spec: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -893,7 +878,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: discovery stackable.tech/vendor: Stackable @@ -910,7 +894,6 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -927,7 +910,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -944,7 +926,6 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -960,7 +941,6 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-serviceaccount ownerReferences: @@ -974,7 +954,6 @@ kind: RoleBinding metadata: labels: app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-rolebinding ownerReferences: @@ -1023,7 +1002,6 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -1056,7 +1034,6 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -1089,7 +1066,6 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase - app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable From c1b4366869390eb0b238335378634d33f0f1bdbb Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 13 Aug 2026 15:25:45 +0200 Subject: [PATCH 4/6] added in wrongly removed labels --- .../controller/build/resource/statefulset.rs | 3 ++- tests/templates/kuttl/smoke/30-assert.yaml.j2 | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rust/operator-binary/src/controller/build/resource/statefulset.rs b/rust/operator-binary/src/controller/build/resource/statefulset.rs index 86bef023..db926cd6 100644 --- a/rust/operator-binary/src/controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/controller/build/resource/statefulset.rs @@ -303,7 +303,8 @@ pub fn build_rolegroup_statefulset( // Listener PVC labels should stay stable across upgrades and so should not // include the version field (see HDFS for a similar pattern). let mut unversioned_labels = cluster.role_group_selector(hbase_role, role_group_name); - // Vendor is not included in role_group_selector labels, so add it. + // Neither vendor nor managed-by are included in role_group_selector + // labels: vendor is a required and so add it back. unversioned_labels.insert(Label::stackable_vendor()); let listener_pvc = diff --git a/tests/templates/kuttl/smoke/30-assert.yaml.j2 b/tests/templates/kuttl/smoke/30-assert.yaml.j2 index b2243107..7dbf9d9b 100644 --- a/tests/templates/kuttl/smoke/30-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-assert.yaml.j2 @@ -11,6 +11,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -45,6 +46,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -71,6 +73,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -105,6 +108,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -131,6 +135,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -165,6 +170,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default prometheus.io/scrape: "true" @@ -193,6 +199,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -217,6 +224,7 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -398,6 +406,7 @@ spec: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -421,6 +430,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -440,6 +450,7 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -630,6 +641,7 @@ spec: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -653,6 +665,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default restarter.stackable.tech/enabled: "true" @@ -677,6 +690,7 @@ spec: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -878,6 +892,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: discovery stackable.tech/vendor: Stackable @@ -894,6 +909,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -910,6 +926,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -926,6 +943,7 @@ metadata: labels: app.kubernetes.io/component: restserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -941,6 +959,7 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-serviceaccount ownerReferences: @@ -954,6 +973,7 @@ kind: RoleBinding metadata: labels: app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase name: test-hbase-rolebinding ownerReferences: @@ -1002,6 +1022,7 @@ metadata: labels: app.kubernetes.io/component: master app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable @@ -1034,6 +1055,7 @@ metadata: labels: app.kubernetes.io/component: regionserver app.kubernetes.io/instance: test-hbase + app.kubernetes.io/managed-by: hbase.stackable.tech_hbasecluster app.kubernetes.io/name: hbase app.kubernetes.io/role-group: default stackable.tech/vendor: Stackable From 83ca24e43d4ddf8a588cfa6bf6a45a1e81ac00c9 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Fri, 14 Aug 2026 14:38:23 +0200 Subject: [PATCH 5/6] added missing PR number to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f29153fc..051a8e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ which could cause problems with GitOps tools (e.g. ArgoCD) reporting a diff in the custom resources. See [our internal issue](https://github.com/stackabletech/hdfs-operator/issues/626) and [the fix](https://github.com/kube-rs/kube/pull/2042) for details ([#792]). - BREAKING: The app.kubernetes.io/managed-by label value changed from hbase.stackable.com_hbasecluster to - hbase.stackable.tech_hbasecluster, aligning with all other operators. + hbase.stackable.tech_hbasecluster, aligning with all other operators ([#795]). - BREAKING: The rest-server listener PVC template now carries only the unversioned selector labels. Existing rest-server StatefulSets must be deleted once before the new operator can reconcile them ([#795]). From 1b85652c7a3c9aeab29802bb0bd800781e67ba1c Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Fri, 14 Aug 2026 19:05:55 +0200 Subject: [PATCH 6/6] add early exit tests --- Cargo.lock | 1 + Cargo.nix | 4 + Cargo.toml | 1 + .../templates/clusterrole-operator.yaml | 10 +- rust/operator-binary/Cargo.toml | 1 + rust/operator-binary/src/hbase_controller.rs | 132 ++++++++++++++ rust/operator-binary/src/main.rs | 20 ++- .../kuttl/cluster-operation/40-assert.yaml | 170 ++++++++++++++++++ .../40-delete-owned-resources.yaml | 57 ++++++ tests/templates/kuttl/smoke/30-assert.yaml.j2 | 6 +- .../kuttl/smoke/30-install-hbase.yaml.j2 | 5 + 11 files changed, 400 insertions(+), 7 deletions(-) create mode 100644 tests/templates/kuttl/cluster-operation/40-assert.yaml create mode 100644 tests/templates/kuttl/cluster-operation/40-delete-owned-resources.yaml diff --git a/Cargo.lock b/Cargo.lock index 36c7760f..b8c3c7df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3192,6 +3192,7 @@ dependencies = [ "const_format", "fnv", "futures 0.3.33", + "http", "indoc", "rstest", "serde", diff --git a/Cargo.nix b/Cargo.nix index 23cd76f0..281719e0 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -10521,6 +10521,10 @@ rec { } ]; devDependencies = [ + { + name = "http"; + packageId = "http"; + } { name = "rstest"; packageId = "rstest"; diff --git a/Cargo.toml b/Cargo.toml index 091afb99..0aa6b605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ clap = "4.6" const_format = "0.2" fnv = "1.0" futures = { version = "0.3", features = ["compat"] } +http = "1.3" indoc = "2.0" rstest = "0.26" serde = { version = "1.0", features = ["derive"] } diff --git a/deploy/helm/hbase-operator/templates/clusterrole-operator.yaml b/deploy/helm/hbase-operator/templates/clusterrole-operator.yaml index 6b0fc340..af1b9062 100644 --- a/deploy/helm/hbase-operator/templates/clusterrole-operator.yaml +++ b/deploy/helm/hbase-operator/templates/clusterrole-operator.yaml @@ -31,7 +31,7 @@ rules: - patch - watch # ServiceAccount created per HbaseCluster for workload pod identity. - # Applied via SSA and tracked for orphan cleanup. + # Applied via SSA, tracked for orphan cleanup and watched by the controller. - apiGroups: - "" resources: @@ -42,8 +42,9 @@ rules: - get - list - patch + - watch # RoleBinding created per HbaseCluster to bind the product ClusterRole to the workload - # ServiceAccount. Applied via SSA and tracked for orphan cleanup. + # ServiceAccount. Applied via SSA, tracked for orphan cleanup and watched by the controller. - apiGroups: - rbac.authorization.k8s.io resources: @@ -54,6 +55,7 @@ rules: - get - list - patch + - watch # Required to bind the product ClusterRole to the per-cluster ServiceAccount. - apiGroups: - rbac.authorization.k8s.io @@ -75,7 +77,8 @@ rules: - list - patch - watch - # PodDisruptionBudget created per role. Applied via SSA and tracked for orphan cleanup. + # PodDisruptionBudget created per role. Applied via SSA, tracked for orphan cleanup and + # watched by the controller. - apiGroups: - policy resources: @@ -86,6 +89,7 @@ rules: - get - list - patch + - watch # Required for maintaining the CRDs within the operator (including the conversion webhook info). # Also for the startup condition check before the controller can run. - apiGroups: diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index 902fedb0..472acb10 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -29,5 +29,6 @@ tracing.workspace = true built.workspace = true [dev-dependencies] +http.workspace = true rstest.workspace = true serde_yaml.workspace = true diff --git a/rust/operator-binary/src/hbase_controller.rs b/rust/operator-binary/src/hbase_controller.rs index 6b6751d2..9ae1551d 100644 --- a/rust/operator-binary/src/hbase_controller.rs +++ b/rust/operator-binary/src/hbase_controller.rs @@ -12,6 +12,7 @@ use stackable_operator::{ cli::OperatorEnvironmentOptions, cluster_resources::ClusterResourceApplyStrategy, kube::{ + Resource, core::{DeserializeGuard, error_boundary}, runtime::controller::Action, }, @@ -76,6 +77,10 @@ pub async fn reconcile_hbase( ) -> Result { tracing::info!("Starting reconcile"); + if hbase.meta().deletion_timestamp.is_some() { + return Ok(Action::await_change()); + } + let hbase = hbase .0 .as_ref() @@ -126,3 +131,130 @@ pub fn error_policy( _ => Action::requeue(*Duration::from_secs(5)), } } + +#[cfg(test)] +mod tests { + use stackable_operator::{ + client::Client, + kube::{Client as KubeClient, Config}, + }; + + use super::*; + use crate::test_utils; + + /// A [`Ctx`] whose client points at a closed port. Any API call made through it fails the + /// reconciliation, so an `Ok` result proves the reconciler returned before touching the + /// Kubernetes API. + fn unreachable_ctx() -> Arc { + let config = Config::new( + "http://127.0.0.1:1" + .parse::() + .expect("valid static URI"), + ); + let kube_client = KubeClient::try_from(config).expect("client from static config"); + + Arc::new(Ctx { + client: Client::new( + kube_client, + None, + "default".to_owned(), + test_utils::cluster_info(), + ), + operator_environment: OperatorEnvironmentOptions { + operator_namespace: "stackable-operators".to_owned(), + operator_service_name: "hbase-operator".to_owned(), + image_repository: "oci.stackable.tech/sdp".to_owned(), + }, + }) + } + + /// Drives the async reconciler from the synchronous tests used in this repo. + /// The [`Ctx`] is built inside `block_on` because the kube client needs a running reactor + /// already at construction time. + fn reconcile(hbase: DeserializeGuard) -> Result { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("current-thread tokio runtime") + .block_on(async { reconcile_hbase(Arc::new(hbase), unreachable_ctx()).await }) + } + + #[test] + fn reconcile_exits_early_for_deleted_cluster() { + let hbase = serde_yaml::from_str( + r#" +apiVersion: hbase.stackable.tech/v1alpha1 +kind: HbaseCluster +metadata: + name: hbase + namespace: default + deletionTimestamp: "2026-08-14T12:00:00Z" +spec: + image: + productVersion: 2.6.3 + clusterConfig: + hdfsConfigMapName: simple-hdfs + zookeeperConfigMapName: simple-znode +"#, + ) + .expect("valid HbaseCluster YAML"); + + let action = reconcile(hbase).expect("a deleted cluster reconciles without any API call"); + + assert_eq!(action, Action::await_change()); + } + + #[test] + fn reconcile_exits_early_for_deleted_cluster_with_invalid_spec() { + // The spec is missing all required fields, so the DeserializeGuard captures a + // deserialization error. During deletion the spec is irrelevant and the reconciler must + // still exit quietly instead of erroring through the whole teardown. + let hbase = serde_yaml::from_str( + r#" +apiVersion: hbase.stackable.tech/v1alpha1 +kind: HbaseCluster +metadata: + name: hbase + namespace: default + deletionTimestamp: "2026-08-14T12:00:00Z" +spec: {} +"#, + ) + .expect("YAML parses; the invalid spec is captured inside the DeserializeGuard"); + + let action = + reconcile(hbase).expect("a deleted cluster reconciles even when its spec is invalid"); + + assert_eq!(action, Action::await_change()); + } + + #[test] + fn reconcile_proceeds_for_live_cluster() { + // Without a deletion timestamp the reconciler must not exit early: it proceeds to + // dereference, which fails against the unreachable test API server. + let hbase = serde_yaml::from_str( + r#" +apiVersion: hbase.stackable.tech/v1alpha1 +kind: HbaseCluster +metadata: + name: hbase + namespace: default +spec: + image: + productVersion: 2.6.3 + clusterConfig: + hdfsConfigMapName: simple-hdfs + zookeeperConfigMapName: simple-znode +"#, + ) + .expect("valid HbaseCluster YAML"); + + let result = reconcile(hbase); + + assert!( + matches!(result, Err(Error::Dereference { .. })), + "a live cluster must reach the API (and fail dereferencing against the unreachable \ + test server), not exit early: {result:?}" + ); + } +} diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index dec10057..dd487edd 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -14,7 +14,9 @@ use stackable_operator::{ eos::EndOfSupportChecker, k8s_openapi::api::{ apps::v1::StatefulSet, - core::v1::{ConfigMap, Service}, + core::v1::{ConfigMap, Service, ServiceAccount}, + policy::v1::PodDisruptionBudget, + rbac::v1::RoleBinding, }, kube::{ CustomResourceExt, ResourceExt, @@ -134,6 +136,22 @@ async fn main() -> anyhow::Result<()> { watch_namespace.get_api::(&client), watcher::Config::default(), ) + .owns( + watch_namespace.get_api::(&client), + watcher::Config::default(), + ) + .owns( + watch_namespace.get_api::(&client), + watcher::Config::default(), + ) + .owns( + watch_namespace.get_api::(&client), + watcher::Config::default(), + ) + .owns( + watch_namespace.get_api::(&client), + watcher::Config::default(), + ) .watches( watch_namespace.get_api::>(&client), watcher::Config::default(), diff --git a/tests/templates/kuttl/cluster-operation/40-assert.yaml b/tests/templates/kuttl/cluster-operation/40-assert.yaml new file mode 100644 index 00000000..4101abe2 --- /dev/null +++ b/tests/templates/kuttl/cluster-operation/40-assert.yaml @@ -0,0 +1,170 @@ +--- +# The recreated StatefulSets must bring the cluster back to ready, and the recreated +# objects must carry an owner reference back to the HbaseCluster so that garbage +# collection still works for them. +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: recreate-owned-resources +timeout: 300 +commands: + - script: kubectl -n $NAMESPACE wait --for=condition=available hbaseclusters.hbase.stackable.tech/test-hbase --timeout 301s +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-hbase-master-default + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-hbase-regionserver-default + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-hbase-restserver-default + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-hbase-serviceaccount + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: test-hbase-rolebinding + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: test-hbase-master + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: test-hbase-regionserver + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: test-hbase-restserver + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-hbase + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-master-default-headless + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-master-default-metrics + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-regionserver-default-headless + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-regionserver-default-metrics + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-restserver-default-headless + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase +--- +apiVersion: v1 +kind: Service +metadata: + name: test-hbase-restserver-default-metrics + ownerReferences: + - apiVersion: hbase.stackable.tech/v1alpha1 + controller: true + kind: HbaseCluster + name: test-hbase diff --git a/tests/templates/kuttl/cluster-operation/40-delete-owned-resources.yaml b/tests/templates/kuttl/cluster-operation/40-delete-owned-resources.yaml new file mode 100644 index 00000000..7fc2b5b3 --- /dev/null +++ b/tests/templates/kuttl/cluster-operation/40-delete-owned-resources.yaml @@ -0,0 +1,57 @@ +--- +# The operator owns (`.owns()` in main.rs) every resource it applies. Deleting one must +# trigger a reconcile that recreates it — this is the behavioural check for the watch +# registrations and the `watch` RBAC verbs in the operator ClusterRole. It lives in this +# test because deleting the StatefulSets forces a full pod-restart cycle, and this test +# cycles the pods anyway. +# +# Recreation is proven by UID change: waiting for mere existence could pass without any +# deletion having happened. TestStep commands run exactly once (no kuttl retry loop), so +# the polling below stays quiet in the log. +# +# Owned resources deliberately NOT tested here: the role-group ConfigMaps +# (test-hbase-{master,regionserver,restserver}-default). The same `.owns(ConfigMap)` +# registration is already exercised by the discovery ConfigMap below, and they are +# mounted into running pods, which a deletion window would disturb. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: delete-owned-resources +timeout: 300 +commands: + - script: | + set -eu + + delete_and_await_recreation() { + kind=$1 + name=$2 + old_uid=$(kubectl get -n "$NAMESPACE" "$kind" "$name" -o jsonpath='{.metadata.uid}') + kubectl delete -n "$NAMESPACE" "$kind" "$name" --wait=false + # Recreation is a single reconcile away, so this normally succeeds on the + # first iteration; 30s is a generous upper bound well below the step timeout. + for _ in $(seq 1 30); do + new_uid=$(kubectl get -n "$NAMESPACE" "$kind" "$name" -o jsonpath='{.metadata.uid}' 2>/dev/null || true) + if [ -n "$new_uid" ] && [ "$new_uid" != "$old_uid" ]; then + return 0 + fi + sleep 1 + done + echo "$kind/$name was not recreated (old uid: $old_uid, current: '${new_uid:-}')" >&2 + return 1 + } + + delete_and_await_recreation serviceaccount test-hbase-serviceaccount + delete_and_await_recreation rolebinding test-hbase-rolebinding + delete_and_await_recreation poddisruptionbudget test-hbase-master + delete_and_await_recreation poddisruptionbudget test-hbase-regionserver + delete_and_await_recreation poddisruptionbudget test-hbase-restserver + delete_and_await_recreation configmap test-hbase + delete_and_await_recreation service test-hbase-master-default-headless + delete_and_await_recreation service test-hbase-master-default-metrics + delete_and_await_recreation service test-hbase-regionserver-default-headless + delete_and_await_recreation service test-hbase-regionserver-default-metrics + delete_and_await_recreation service test-hbase-restserver-default-headless + delete_and_await_recreation service test-hbase-restserver-default-metrics + delete_and_await_recreation statefulset test-hbase-master-default + delete_and_await_recreation statefulset test-hbase-regionserver-default + delete_and_await_recreation statefulset test-hbase-restserver-default diff --git a/tests/templates/kuttl/smoke/30-assert.yaml.j2 b/tests/templates/kuttl/smoke/30-assert.yaml.j2 index 7dbf9d9b..482138a0 100644 --- a/tests/templates/kuttl/smoke/30-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-assert.yaml.j2 @@ -384,7 +384,7 @@ spec: securityContext: fsGroup: 1000 serviceAccountName: test-hbase-serviceaccount - terminationGracePeriodSeconds: 1200 + terminationGracePeriodSeconds: 60 volumes: - configMap: name: test-hbase-master-default @@ -619,7 +619,7 @@ spec: securityContext: fsGroup: 1000 serviceAccountName: test-hbase-serviceaccount - terminationGracePeriodSeconds: 3600 + terminationGracePeriodSeconds: 60 volumes: - configMap: name: test-hbase-regionserver-default @@ -850,7 +850,7 @@ spec: securityContext: fsGroup: 1000 serviceAccountName: test-hbase-serviceaccount - terminationGracePeriodSeconds: 300 + terminationGracePeriodSeconds: 60 volumes: - configMap: name: test-hbase-restserver-default diff --git a/tests/templates/kuttl/smoke/30-install-hbase.yaml.j2 b/tests/templates/kuttl/smoke/30-install-hbase.yaml.j2 index 7535a3e8..8c28c42f 100644 --- a/tests/templates/kuttl/smoke/30-install-hbase.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-install-hbase.yaml.j2 @@ -20,6 +20,9 @@ spec: {% endif %} masters: config: + # Test-only: the production default (20m) becomes the pods' terminationGracePeriodSeconds, + # which the namespace controller uses to schedule its deletion retries. + gracefulShutdownTimeout: 1m logging: enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }} listenerClass: {{ test_scenario['values']['listener-class'] }} @@ -32,6 +35,7 @@ spec: replicas: 2 regionServers: config: + gracefulShutdownTimeout: 1m logging: enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }} listenerClass: {{ test_scenario['values']['listener-class'] }} @@ -44,6 +48,7 @@ spec: replicas: 2 restServers: config: + gracefulShutdownTimeout: 1m logging: enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }} listenerClass: {{ test_scenario['values']['listener-class'] }}