From 8c4c85a750d258c9a71935633fe03eb30e0c5067 Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Thu, 19 Mar 2026 11:59:48 +0530 Subject: [PATCH] OADP-7017: fix DPA error when podConfig has no nodeSelector with loadAffinity The validator was incorrectly triggering an error whenever podConfig was set (even with only resourceAllocations) alongside nodeAgent.loadAffinity that used matchExpressions. The cross-validation between podConfig and loadAffinityConfig should only run when podConfig.nodeSelector is explicitly set, since that is the only field being compared. Add a guard of len(PodConfig.NodeSelector) > 0 to the outer condition so that a podConfig containing only resourceAllocations (or other non-selector fields) is valid alongside any loadAffinity configuration. Made-with: Cursor --- internal/controller/validator.go | 2 ++ internal/controller/validator_test.go | 42 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/internal/controller/validator.go b/internal/controller/validator.go index 482130daa3b..1a8a47a95ed 100644 --- a/internal/controller/validator.go +++ b/internal/controller/validator.go @@ -70,8 +70,10 @@ func (r *DataProtectionApplicationReconciler) ValidateDataProtectionCR(log logr. // Ensure DPA spec.configuration.nodeAgent.PodConfig is not different from spec.configuration.nodeAgent.LoadAffinityConfig // If LoadAffinityConfig is set, it will be used instead of PodConfig; however, if both are set, they must be identical. + // Only validate when podConfig.nodeSelector is explicitly set; an empty podConfig (e.g. only resourceAllocations) is valid alongside loadAffinity. if r.dpa.Spec.Configuration.NodeAgent != nil && r.dpa.Spec.Configuration.NodeAgent.PodConfig != nil && + len(r.dpa.Spec.Configuration.NodeAgent.PodConfig.NodeSelector) > 0 && r.dpa.Spec.Configuration.NodeAgent.LoadAffinityConfig != nil { if len(r.dpa.Spec.Configuration.NodeAgent.LoadAffinityConfig) > 1 { diff --git a/internal/controller/validator_test.go b/internal/controller/validator_test.go index 063cb69b695..02e5ef2df7b 100644 --- a/internal/controller/validator_test.go +++ b/internal/controller/validator_test.go @@ -2466,6 +2466,48 @@ func TestDPAReconciler_ValidateDataProtectionCR(t *testing.T) { }, wantErr: false, }, + { + // OADP-7017: podConfig with resourceAllocations but no nodeSelector alongside loadAffinity using matchExpressions should be valid + name: "[valid] PodConfig with resourceAllocations but no nodeSelector and LoadAffinityConfig with matchExpressions only", + dpa: &oadpv1alpha1.DataProtectionApplication{ + Spec: oadpv1alpha1.DataProtectionApplicationSpec{ + BackupImages: ptr.To(false), + Configuration: &oadpv1alpha1.ApplicationConfig{ + Velero: &oadpv1alpha1.VeleroConfig{ + NoDefaultBackupLocation: true, + }, + NodeAgent: &oadpv1alpha1.NodeAgentConfig{ + NodeAgentCommonFields: oadpv1alpha1.NodeAgentCommonFields{ + PodConfig: &oadpv1alpha1.PodConfig{ + ResourceAllocations: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + }, + }, + }, + NodeAgentConfigMapSettings: oadpv1alpha1.NodeAgentConfigMapSettings{ + LoadAffinityConfig: []*oadpv1alpha1.LoadAffinity{ + { + NodeSelector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "foo", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"bar"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantErr: false, + }, { name: "[valid] Only LoadAffinityConfig is set with MatchExpressions", dpa: &oadpv1alpha1.DataProtectionApplication{