diff --git a/CHANGELOG.md b/CHANGELOG.md index 577678ef..2584a815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,10 @@ All notable changes to this project will be documented in this file. discarded when the application references a SparkApplicationTemplate. The overrides of the template are now applied first and the ones of the SparkApplication on top of them, so an application can also remove a JVM argument that one of its templates added ([#745]). +- BREAKING: The connect and history-server listener PVC templates now carry the recommended + labels without the version label, so that the labels stay stable across upgrades. + Existing connect and history-server StatefulSets must be deleted once before the new operator + can reconcile them (connect server: [#750], history server: [#753]). [#721]: https://github.com/stackabletech/spark-k8s-operator/pull/721 [#727]: https://github.com/stackabletech/spark-k8s-operator/pull/727 @@ -45,6 +49,7 @@ All notable changes to this project will be documented in this file. [#744]: https://github.com/stackabletech/spark-k8s-operator/pull/744 [#745]: https://github.com/stackabletech/spark-k8s-operator/pull/745 [#746]: https://github.com/stackabletech/spark-k8s-operator/pull/746 +[#750]: https://github.com/stackabletech/spark-k8s-operator/pull/750 [#753]: https://github.com/stackabletech/spark-k8s-operator/pull/753 ## [26.7.0] - 2026-07-21 diff --git a/rust/operator-binary/src/connect/controller/build/mod.rs b/rust/operator-binary/src/connect/controller/build/mod.rs index 5dffa384..2a43fc9a 100644 --- a/rust/operator-binary/src/connect/controller/build/mod.rs +++ b/rust/operator-binary/src/connect/controller/build/mod.rs @@ -166,6 +166,25 @@ pub(crate) fn recommended_labels_for_role_resources( ) } +/// Recommended labels for role resources which cannot be mutated and should therefore not +/// include the product version, like the listener PVC in the StatefulSet's +/// `volumeClaimTemplates`. +/// +/// Spark Connect has no role groups, so all role resources use these labels (without a role +/// group label). +pub(crate) fn recommended_labels_for_unversioned_role_resources( + server: &ValidatedSparkConnectServer, + role_name: &RoleName, +) -> Labels { + Labels::from_iter([ + label::label_app_kubernetes_io_instance(&server.name), + label::label_app_kubernetes_io_name(&PRODUCT_NAME), + label::label_app_kubernetes_io_component(role_name), + label::label_app_kubernetes_io_managed_by(&OPERATOR_NAME, &CONTROLLER_NAME), + label::label_stackable_tech_vendor(), + ]) +} + /// Selector labels matching the pods of the given role. pub(crate) fn role_selector(server: &ValidatedSparkConnectServer, role_name: &RoleName) -> Labels { label::role_selector(&server.name, &PRODUCT_NAME, role_name) diff --git a/rust/operator-binary/src/connect/controller/build/server.rs b/rust/operator-binary/src/connect/controller/build/server.rs index 419bfefc..007d3307 100644 --- a/rust/operator-binary/src/connect/controller/build/server.rs +++ b/rust/operator-binary/src/connect/controller/build/server.rs @@ -44,7 +44,10 @@ use crate::{ GRPC, HTTP, common::{self, SparkConnectRole, object_name}, controller::{ - build::{object_meta, recommended_labels_for_role_resources, role_selector}, + build::{ + object_meta, recommended_labels_for_role_resources, + recommended_labels_for_unversioned_role_resources, role_selector, + }, validate::ValidatedSparkConnectServer, }, crd::{ @@ -289,6 +292,11 @@ pub(crate) fn build_stateful_set( )); } + // Used for the listener PVC template, which cannot be modified once it is deployed. The + // version label is omitted so the labels stay stable across version upgrades. + let unversioned_recommended_labels = + recommended_labels_for_unversioned_role_resources(validated, &SparkConnectRole::Server); + // Add listener volume // Listener endpoints for the Webserver role will use persistent volumes // so that load balancers can hard-code the target addresses. This will @@ -297,7 +305,7 @@ pub(crate) fn build_stateful_set( let volume_claim_templates = Some(vec![ ListenerOperatorVolumeSourceBuilder::new( &ListenerReference::ListenerName(listener_name.to_string()), - &recommended_labels, + &unversioned_recommended_labels, ) .build_pvc(LISTENER_VOLUME_NAME.to_string()) .context(BuildListenerVolumeSnafu)?, diff --git a/tests/templates/kuttl/smoke/50-assert.yaml b/tests/templates/kuttl/smoke/50-assert.yaml index 1319f33a..a6f2e4ca 100644 --- a/tests/templates/kuttl/smoke/50-assert.yaml +++ b/tests/templates/kuttl/smoke/50-assert.yaml @@ -11,5 +11,11 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert commands: - script: | + # Wait for the spark-submit pod: the image pull can take minutes and + # kuttl would otherwise retry (and log) this whole script every second. + kubectl wait -n $NAMESPACE pod \ + --selector batch.kubernetes.io/job-name=spark-pi-s3-1 \ + --for=jsonpath='{.status.phase}'=Running \ + --timeout=280s > /dev/null 2>&1 || exit 1 SPARK_SUBMIT_POD=$(kubectl get -n $NAMESPACE pods --field-selector=status.phase=Running --selector batch.kubernetes.io/job-name=spark-pi-s3-1 -o jsonpath='{.items[0].metadata.name}') kubectl exec -n $NAMESPACE --container spark-submit $SPARK_SUBMIT_POD -- cat /stackable/log/containerdebug-state.json | jq --exit-status '"valid JSON"'