Dedup the entries in backuprequest.spec.excludeNamespaces (#10562)
Run the E2E test on kind / setup-test-matrix (push) Failing after 4s
e2e-test-kind.yaml / extract (push) Failing after 8s
Run the E2E test on kind / get-go-version (push) Failing after 9s
Run the E2E test on kind / build (push) Skipped
Run the E2E test on kind / run-e2e-test (push) Skipped
push.yml / extract (push) Failing after 5s
Scorecard supply-chain security / Scorecard analysis (push) Skipped
Main CI / get-go-version (push) Failing after 6s
Main CI / Build (push) Skipped

This commit fixes the issue that when a namespace with label velero.io/exclude-from-backup: "true"
is added to the exludeNamespaces of a backup CR.  There will be
duplicated entries of the namespace in the spec of the backup.

Signed-off-by: Daniel Jiang <daniel.jiang@broadcom.com>
This commit is contained in:
Daniel Jiang
2026-09-24 15:02:47 +08:00
committed by GitHub
parent 4407481a9b
commit dfabab70bf
3 changed files with 122 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
Dedup the entries in backuprequest.spec.excludeNamespaces
+4
View File
@@ -544,6 +544,10 @@ func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *vel
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("error getting namespace list: %v", err))
}
if len(request.Spec.ExcludedNamespaces) > 0 {
request.Spec.ExcludedNamespaces = sets.NewString(request.Spec.ExcludedNamespaces...).List()
}
// validate whether Included/Excluded resources and IncludedClusterResource are mixed with
// Included/Excluded cluster-scoped/namespace-scoped resources.
if oldAndNewFilterParametersUsedTogether(request.Spec) {
+117
View File
@@ -2901,3 +2901,120 @@ func TestPrepareBackupRequest_FilterPoliciesWithNewFilters(t *testing.T) {
})
}
}
func TestPrepareBackupRequest_DeduplicateExcludedNamespaces(t *testing.T) {
tests := []struct {
name string
specExcluded []string
clusterNamespaces []*corev1api.Namespace
expectedExcluded []string
}{
{
name: "duplicates in spec.excludedNamespaces are de-duped",
specExcluded: []string{"ns-1", "ns-2", "ns-1", "ns-2", "ns-3"},
clusterNamespaces: []*corev1api.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-4",
},
},
},
expectedExcluded: []string{"ns-1", "ns-2", "ns-3"},
},
{
name: "labeled namespaces overlapping with spec.excludedNamespaces are de-duped",
specExcluded: []string{"ns-1", "ns-2"},
clusterNamespaces: []*corev1api.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-2",
Labels: map[string]string{
velerov1api.ExcludeFromBackupLabel: "true",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-3",
Labels: map[string]string{
velerov1api.ExcludeFromBackupLabel: "true",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-4",
},
},
},
expectedExcluded: []string{"ns-1", "ns-2", "ns-3"},
},
{
name: "empty spec.excludedNamespaces with labeled namespaces",
specExcluded: nil,
clusterNamespaces: []*corev1api.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-1",
Labels: map[string]string{
velerov1api.ExcludeFromBackupLabel: "true",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "ns-2",
Labels: map[string]string{
velerov1api.ExcludeFromBackupLabel: "true",
},
},
},
},
expectedExcluded: []string{"ns-1", "ns-2"},
},
{
name: "empty spec.excludedNamespaces with no labeled namespaces remains empty",
specExcluded: nil,
clusterNamespaces: nil,
expectedExcluded: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
formatFlag := logging.FormatText
logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag)
backupLocation := builder.ForBackupStorageLocation(velerov1api.DefaultNamespace, "loc-1").
Phase(velerov1api.BackupStorageLocationPhaseAvailable).Result()
var objects []runtime.Object
objects = append(objects, backupLocation)
for _, ns := range tt.clusterNamespaces {
objects = append(objects, ns)
}
fakeClient := velerotest.NewFakeControllerRuntimeClient(t, objects...)
apiServer := velerotest.NewAPIServer(t)
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
require.NoError(t, err)
c := &backupReconciler{
logger: logger,
discoveryHelper: discoveryHelper,
kbClient: fakeClient,
clock: &clock.RealClock{},
formatFlag: formatFlag,
}
backup := defaultBackup().StorageLocation("loc-1").Result()
backup.Spec.ExcludedNamespaces = tt.specExcluded
res := c.prepareBackupRequest(t.Context(), backup, logger)
defer res.WorkerPool.Stop()
assert.Empty(t, res.Status.ValidationErrors)
assert.Equal(t, tt.expectedExcluded, res.Spec.ExcludedNamespaces)
})
}
}