From e16ff5ba3ec33fa2ccf60ab03ab0690b9cd9d5d9 Mon Sep 17 00:00:00 2001 From: sreedev Date: Thu, 23 Apr 2026 14:21:53 +0530 Subject: [PATCH] Allow VolumeAttributesClassName changes in storageConfig webhook VolumeAttributesClassName (VAC) is used for in-place volume performance class changes (e.g. AWS EBS VolumeAttributesClass) and does not replace the PVC. The webhook was blocking all changes to CassandraDataVolumeClaimSpec beyond storage size, which prevented legitimate VAC updates. Normalize VolumeAttributesClassName before the DeepEqual check so changes to it are always permitted. Also consolidates the two separate nil guards for CassandraDataVolumeClaimSpec into one block, fixing a latent nil panic if AllowStorageChangesAnnotation was set on a DC with no StorageConfig. --- CHANGELOG.md | 1 + .../v1beta1/cassandradatacenter_webhook.go | 13 ++-- .../cassandradatacenter_webhook_test.go | 76 +++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d308f0..b225eeaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Changelog for Cass Operator, new PRs should update the `main / unreleased` secti ## unreleased +* [ENHANCEMENT] [#922](https://github.com/k8ssandra/cass-operator/issues/922) Allow VolumeAttributesClassName changes in storageConfig webhook to support in-place EBS volume performance class updates on AWS * [ENHANCEMENT] [#912](https://github.com/k8ssandra/cass-operator/issues/912) Add new webhook validations for maxUnavailable string format as well as PVC sizes * [ENHANCEMENT] [#902](https://github.com/k8ssandra/cass-operator/issues/902) If scaling down or scaling up process is still ongoing, the webhook will prevent changing the cluster size. diff --git a/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook.go b/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook.go index 99766b2a..e5e570ca 100644 --- a/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook.go +++ b/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook.go @@ -204,12 +204,15 @@ func ValidateDatacenterFieldChanges(oldDc *api.CassandraDatacenter, newDc *api.C return attemptedTo( "shrink storageConfig.CassandraDataVolumeClaimSpec from %s to %s", oldStorageRequest.String(), newStorageRequest.String()) } - } - // CassandraDataVolumeClaimSpec changes are disallowed - if metav1.HasAnnotation(newDc.ObjectMeta, api.AllowStorageChangesAnnotation) && newDc.Annotations[api.AllowStorageChangesAnnotation] == "true" { - // If the AllowStorageChangesAnnotation is set, we allow changes to the CassandraDataVolumeClaimSpec sizes, but not other fields - oldClaimSpec.Resources.Requests = newClaimSpec.Resources.Requests + if metav1.HasAnnotation(newDc.ObjectMeta, api.AllowStorageChangesAnnotation) && newDc.Annotations[api.AllowStorageChangesAnnotation] == "true" { + // If the AllowStorageChangesAnnotation is set, we allow changes to the CassandraDataVolumeClaimSpec sizes, but not other fields + oldClaimSpec.Resources.Requests = newClaimSpec.Resources.Requests + } + + // VolumeAttributesClassName changes are always allowed as they represent in-place volume + // performance class changes (e.g. AWS EBS VolumeAttributesClass) and do not replace the PVC. + oldClaimSpec.VolumeAttributesClassName = newClaimSpec.VolumeAttributesClassName } if !apiequality.Semantic.DeepEqual(oldClaimSpec, newClaimSpec) { diff --git a/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook_test.go b/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook_test.go index b805a835..1df29d8a 100644 --- a/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook_test.go +++ b/internal/webhooks/cassandra/v1beta1/cassandradatacenter_webhook_test.go @@ -786,6 +786,82 @@ func Test_ValidateDatacenterFieldChanges(t *testing.T) { }, errString: "", }, + { + name: "VolumeAttributesClassName change is allowed", + oldDc: &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleDC", + }, + Spec: api.CassandraDatacenterSpec{ + StorageConfig: api.StorageConfig{ + CassandraDataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ + StorageClassName: storageName, + AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, + VolumeAttributesClassName: ptr.To[string]("gp3-cassandra-general"), + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{"storage": storageSize}, + }, + }, + }, + }, + }, + newDc: &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleDC", + }, + Spec: api.CassandraDatacenterSpec{ + StorageConfig: api.StorageConfig{ + CassandraDataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ + StorageClassName: storageName, + AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, + VolumeAttributesClassName: ptr.To[string]("gp3-cassandra"), + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{"storage": storageSize}, + }, + }, + }, + }, + }, + errString: "", + }, + { + name: "VolumeAttributesClassName change with other field change is rejected", + oldDc: &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleDC", + }, + Spec: api.CassandraDatacenterSpec{ + StorageConfig: api.StorageConfig{ + CassandraDataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ + StorageClassName: storageName, + AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, + VolumeAttributesClassName: ptr.To[string]("gp3-cassandra-general"), + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{"storage": storageSize}, + }, + }, + }, + }, + }, + newDc: &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleDC", + }, + Spec: api.CassandraDatacenterSpec{ + StorageConfig: api.StorageConfig{ + CassandraDataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ + StorageClassName: storageName, + AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteMany"}, + VolumeAttributesClassName: ptr.To[string]("gp3-cassandra"), + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{"storage": storageSize}, + }, + }, + }, + }, + }, + errString: "change storageConfig.CassandraDataVolumeClaimSpec", + }, { name: "Removing a rack", oldDc: &api.CassandraDatacenter{