From ff136e24f89fdd56e8f514396b74c5f324de7f7e 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 6ff0c5c3979..e550c642b6b 100644 --- a/internal/controller/validator.go +++ b/internal/controller/validator.go @@ -72,8 +72,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 25415b139a6..6812fb15c63 100644 --- a/internal/controller/validator_test.go +++ b/internal/controller/validator_test.go @@ -2392,6 +2392,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{