Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ 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 unversioned labels.
Existing connect and history-server StatefulSets must be deleted once before the new operator can reconcile them ([#750]).

[#721]: https://github.com/stackabletech/spark-k8s-operator/pull/721
[#727]: https://github.com/stackabletech/spark-k8s-operator/pull/727
[#732]: https://github.com/stackabletech/spark-k8s-operator/pull/732
[#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

## [26.7.0] - 2026-07-21

Expand Down
5 changes: 4 additions & 1 deletion rust/operator-binary/src/connect/controller/build/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ pub(crate) fn build_stateful_set(
));
}

let unversioned_recommended_labels =
validated.unversioned_recommended_labels(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
Expand All @@ -295,7 +298,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)?,
Expand Down
13 changes: 12 additions & 1 deletion rust/operator-binary/src/connect/controller/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::{
},
s3::ResolvedS3,
},
crd::constants::{CONTAINER_IMAGE_BASE_NAME, OPERATOR_NAME},
crd::constants::{CONTAINER_IMAGE_BASE_NAME, OPERATOR_NAME, UNVERSIONED_PRODUCT_VERSION},
};

#[derive(Snafu, Debug)]
Expand Down Expand Up @@ -179,6 +179,17 @@ impl ValidatedSparkConnectServer {
self.recommended_labels_with(&self.product_version, role_name, role_group_name)
}

/// Recommended labels with a fixed placeholder version, for objects that live in immutable
/// fields (e.g. the listener PVC in the StatefulSet's `volumeClaimTemplates`) and therefore
/// must not carry labels that change on upgrade.
pub fn unversioned_recommended_labels(&self, role: SparkConnectRole) -> Labels {
self.recommended_labels_with(
&UNVERSIONED_PRODUCT_VERSION,
&role.into(),
&DEFAULT_SPARK_CONNECT_ROLE_GROUP,
)
}

/// Recommended labels for a resource of the given role.
pub fn recommended_labels(&self, role: SparkConnectRole) -> Labels {
self.recommended_labels_for(&role.into(), &DEFAULT_SPARK_CONNECT_ROLE_GROUP)
Expand Down
6 changes: 5 additions & 1 deletion rust/operator-binary/src/crd/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{collections::BTreeMap, str::FromStr};
use const_format::concatcp;
use stackable_operator::{
memory::{BinaryMultiple, MemoryQuantity},
v2::types::{common::Port, kubernetes::VolumeName},
v2::types::{common::Port, kubernetes::VolumeName, operator::ProductVersion},
};

pub const APP_NAME: &str = "spark-k8s";
Expand Down Expand Up @@ -101,6 +101,10 @@ stackable_operator::constant!(pub LISTENER_VOLUME_NAME: VolumeName = "listener")
pub const LISTENER_VOLUME_DIR: &str = "/stackable/listener";
pub const DEFAULT_LISTENER_CLASS: &str = "cluster-internal";

// Placeholder product version used for labels on PVC templates, which cannot be modified once
// deployed. A constant value keeps the labels stable across version upgrades.
stackable_operator::constant!(pub UNVERSIONED_PRODUCT_VERSION: ProductVersion = "none");

pub const DEFAULT_SUBMIT_JOB_RETRY_ON_FAILURE_COUNT: u16 = 0;

/// The JVM `security.properties` entries the operator sets by default (DNS cache TTLs).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ pub(crate) fn build_stateful_set(
.context(AddVolumeMountSnafu)?
.build();

let unversioned_recommended_labels = validated.unversioned_recommended_labels(role_group_name);

// 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
Expand All @@ -224,7 +226,7 @@ pub(crate) fn build_stateful_set(
.parse()
.expect("the group listener name is a valid ListenerName"),
),
&recommended_labels,
&unversioned_recommended_labels,
&LISTENER_VOLUME_NAME_PVC,
)]);

Expand Down
13 changes: 12 additions & 1 deletion rust/operator-binary/src/history/controller/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::{
crd::{
constants::{
CONTAINER_IMAGE_BASE_NAME, HISTORY_APP_NAME, HISTORY_CONTROLLER_NAME,
HISTORY_ROLE_NAME, OPERATOR_NAME,
HISTORY_ROLE_NAME, OPERATOR_NAME, UNVERSIONED_PRODUCT_VERSION,
},
history::{HistoryConfig, HistoryConfigFragment, SparkHistoryServerContainer, v1alpha1},
logdir::ResolvedLogDir,
Expand Down Expand Up @@ -231,6 +231,17 @@ impl ValidatedSparkHistoryServer {
self.recommended_labels_with(&self.product_version, role_name, role_group_name)
}

/// Recommended labels with a fixed placeholder version, for objects that live in immutable
/// fields (e.g. the listener PVC in the StatefulSet's `volumeClaimTemplates`) and therefore
/// must not carry labels that change on upgrade.
pub fn unversioned_recommended_labels(&self, role_group_name: &RoleGroupName) -> Labels {
self.recommended_labels_with(
&UNVERSIONED_PRODUCT_VERSION,
&Self::role_name(),
role_group_name,
)
}

fn recommended_labels_with(
&self,
product_version: &ProductVersion,
Expand Down
6 changes: 6 additions & 0 deletions tests/templates/kuttl/smoke/50-assert.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"'
Loading