Skip to content

Fix OADP-7562: handle wildcard in scoped excluded cluster resources#333

Open
mpryc wants to merge 1 commit intomigtools:oadp-devfrom
mpryc:OADP-7562
Open

Fix OADP-7562: handle wildcard in scoped excluded cluster resources#333
mpryc wants to merge 1 commit intomigtools:oadp-devfrom
mpryc:OADP-7562

Conversation

@mpryc
Copy link
Copy Markdown
Collaborator

@mpryc mpryc commented Mar 26, 2026

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Summary by CodeRabbit

  • Bug Fixes
    • Respect wildcard "*" exclusions for backups and restores—controller no longer appends default exclusions when a wildcard is present.
  • Tests
    • Added/expanded tests for wildcard exclusion scenarios and validation of enforced backup fields.
  • Chores
    • Improved controller reliability and type-safety around status updates and request handling; minor code-style/comment cleanup.

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci
Copy link
Copy Markdown

openshift-ci bot commented Mar 26, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mpryc

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@oadp-snyk
Copy link
Copy Markdown

oadp-snyk commented Mar 26, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@mpryc
Copy link
Copy Markdown
Collaborator Author

mpryc commented Mar 26, 2026

/cherry-pick oadp-1.6

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Controllers now avoid appending controller-defined default exclusions when the exclusion list contains the wildcard "*" (now centralized as constant.WildcardString). This change affects Velero backup and restore spec construction; a test was added to verify cluster-scoped exclusions remain ["*"] when provided.

Changes

Cohort / File(s) Summary
Backup exclusion logic
internal/controller/nonadminbackup_controller.go
createVeleroBackupAndSyncWithNonAdminBackup now checks for constant.WildcardString (via slices.Contains) before appending alwaysExcludedNamespacedResources / alwaysExcludedClusterResources to backup exclusion fields.
Restore exclusion logic
internal/controller/nonadminrestore_controller.go
createVeleroRestore now checks for constant.WildcardString before appending "volumesnapshotclasses" to restoreSpec.ExcludedResources (adds slices import).
Wildcard constant
internal/common/constant/constant.go
Introduced exported WildcardString = "*" for Velero resource filter checks.
Tests
internal/controller/nonadminbackup_controller_test.go
Added Ginkgo table case for reconcile create flow with excludedClusterScopedResources: []string{"*"}; asserts NonAdminBackup enters Created and that Velero Backup.Spec.ExcludedClusterScopedResources remains ["*"], while namespace-scoped exclusions include controller defaults.
Validation tests
internal/common/function/function_test.go
Added test case for VolumeGroupSnapshotLabelKey in TestValidateBackupSpecEnforcedFields to exercise enforced-field validation behavior.
Storage location controller hardening
internal/controller/nonadminbackupstoragelocation_controller.go
Converted file comment style; made updateStatusWithRetry and createNonAdminRequest safer with explicit type checks and adjusted conflict-retry flow for status updates.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code with careful paws,
A star "*" held court without a cause.
Where wildcard stands, no defaults creep,
I left its meadow calm to keep.
Small changes, tidy—now I sleep. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main change: handling wildcard ('*') in scoped excluded resources to fix OADP-7562, which matches the core logic modifications across multiple controller files.
Description check ✅ Passed The description includes both required sections: 'Why the changes were made' (explaining the issue and fix with Jira reference) and 'How to test the changes made' (mentioning added unit test coverage).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-cherrypick-robot
Copy link
Copy Markdown

@mpryc: once the present PR merges, I will cherry-pick it on top of oadp-1.6 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick oadp-1.6

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@mpryc
Copy link
Copy Markdown
Collaborator Author

mpryc commented Mar 26, 2026

/hold until it's confirmed to fix issue.

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

  • Fixed backup exclusion handling to properly manage wildcard exclusions. The system now correctly prevents combining wildcard "*" (exclude all) with specific resource exclusion entries, avoiding conflicting backup configurations.

  • Tests

  • Added test coverage for backup exclusion scenarios using wildcard patterns.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/controller/nonadminbackup_controller_test.go (1)

1549-1586: Good test coverage for the wildcard bug fix.

The test correctly validates that:

  1. ExcludedClusterScopedResources: ["*"] is preserved without appending defaults
  2. ExcludedNamespaceScopedResources (without wildcard) still gets the default NAC resources appended

For completeness, consider adding additional test entries:

  • ExcludedNamespaceScopedResources: ["*"] scenario (to verify the symmetric case)
  • Both exclusion lists containing "*" (to verify no defaults are appended to either)
💡 Additional test entry suggestion
ginkgo.Entry("Should not append default excluded resources when wildcard is used in excludedNamespaceScopedResources", nonAdminBackupFullReconcileScenario{
    spec: nacv1alpha1.NonAdminBackupSpec{
        BackupSpec: &velerov1.BackupSpec{
            ExcludedNamespaceScopedResources: []string{"*"},
            IncludedClusterScopedResources:   []string{"persistentvolumes"},
        },
    },
    status: nacv1alpha1.NonAdminBackupStatus{
        Phase: nacv1alpha1.NonAdminPhaseCreated,
        VeleroBackup: &nacv1alpha1.VeleroBackup{
            Namespace: oadpNamespace,
            Status:    nil,
            Spec: &velerov1.BackupSpec{
                ExcludedNamespaceScopedResources: []string{"*"},
                IncludedClusterScopedResources:   []string{"persistentvolumes"},
                ExcludedClusterScopedResources: []string{
                    "securitycontextconstraints",
                    "clusterroles",
                    "clusterrolebindings",
                    "priorityclasses",
                    "customresourcedefinitions",
                    "virtualmachineclusterinstancetypes",
                    "virtualmachineclusterpreferences",
                },
            },
        },
        Conditions: []metav1.Condition{
            {
                Type:    "Accepted",
                Status:  metav1.ConditionTrue,
                Reason:  "BackupAccepted",
                Message: "backup accepted",
            },
            {
                Type:    "Queued",
                Status:  metav1.ConditionTrue,
                Reason:  "BackupScheduled",
                Message: "Created Velero Backup object",
            },
        },
    },
}),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminbackup_controller_test.go` around lines 1549 -
1586, Add two additional ginkgo.Entry test cases to
nonAdminbackup_controller_test.go using the same
nonAdminBackupFullReconcileScenario pattern: one where
BackupSpec.ExcludedNamespaceScopedResources == []string{"*"} to verify defaults
are not appended to namespace-scoped exclusions, and another where both
ExcludedNamespaceScopedResources and ExcludedClusterScopedResources are
[]string{"*"} to verify no defaults are appended to either; ensure each entry
populates the top-level spec.BackupSpec and the nested status.VeleroBackup.Spec
fields (referencing ExcludedNamespaceScopedResources and
ExcludedClusterScopedResources) and includes the same Conditions and Phase as
the existing entries so the test harness (nonAdminBackupFullReconcileScenario,
VeleroBackup.Spec) validates the symmetric wildcard behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/nonadminbackup_controller.go`:
- Around line 709-719: The fallback path unconditionally appends to old-style
ExcludedResources which breaks validation when a user has ExcludedResources:
["*"]; update the logic in the non-admin backup and restore controllers to check
for the wildcard before appending: in the backup flow, guard the two append
calls with if !slices.Contains(backupSpec.ExcludedResources, "*") (targeting the
block that currently appends
alwaysExcludedNamespacedResources/alwaysExcludedClusterResources when scoped
fields are absent), and in the restore flow guard the append with if
!slices.Contains(restoreSpec.ExcludedResources, "*") so nothing is appended when
the wildcard already excludes everything.

---

Nitpick comments:
In `@internal/controller/nonadminbackup_controller_test.go`:
- Around line 1549-1586: Add two additional ginkgo.Entry test cases to
nonAdminbackup_controller_test.go using the same
nonAdminBackupFullReconcileScenario pattern: one where
BackupSpec.ExcludedNamespaceScopedResources == []string{"*"} to verify defaults
are not appended to namespace-scoped exclusions, and another where both
ExcludedNamespaceScopedResources and ExcludedClusterScopedResources are
[]string{"*"} to verify no defaults are appended to either; ensure each entry
populates the top-level spec.BackupSpec and the nested status.VeleroBackup.Spec
fields (referencing ExcludedNamespaceScopedResources and
ExcludedClusterScopedResources) and includes the same Conditions and Phase as
the existing entries so the test harness (nonAdminBackupFullReconcileScenario,
VeleroBackup.Spec) validates the symmetric wildcard behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c38373e3-5363-4f87-921c-73088b1d6efd

📥 Commits

Reviewing files that changed from the base of the PR and between 31a3bfc and 8f848f1.

📒 Files selected for processing (2)
  • internal/controller/nonadminbackup_controller.go
  • internal/controller/nonadminbackup_controller_test.go

Comment on lines +709 to +719
// Skip appending when wildcard '*' is already present, as it already
// excludes everything and mixing '*' with specific items causes Velero
// FailedValidation.
if !slices.Contains(backupSpec.ExcludedNamespaceScopedResources, "*") {
backupSpec.ExcludedNamespaceScopedResources = append(backupSpec.ExcludedNamespaceScopedResources,
alwaysExcludedNamespacedResources...)
}
if !slices.Contains(backupSpec.ExcludedClusterScopedResources, "*") {
backupSpec.ExcludedClusterScopedResources = append(backupSpec.ExcludedClusterScopedResources,
alwaysExcludedClusterResources...)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if the old-style ExcludedResources field also supports wildcards in Velero
# and find all similar append patterns that might need the same fix

# Find other places where resources are appended to exclusion lists without wildcard check
rg -n -A3 -B3 'ExcludedResources\s*=\s*append' --type=go

# Check Velero documentation/validation for wildcard handling in ExcludedResources
rg -n 'ExcludedResources.*\*' --type=go

Repository: migtools/oadp-non-admin

Length of output: 1363


🏁 Script executed:

# Get context around the fallback path in backup controller
sed -n '700,730p' internal/controller/nonadminbackup_controller.go

Repository: migtools/oadp-non-admin

Length of output: 1556


🏁 Script executed:

# Get context around the restore controller append
sed -n '340,360p' internal/controller/nonadminrestore_controller.go

Repository: migtools/oadp-non-admin

Length of output: 873


🏁 Script executed:

# Check if there are any tests or comments that mention ExcludedResources wildcard behavior
rg -i 'excludedresources.*wildcard|wildcard.*excludedresources' --type=go -C 3

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Search for ExcludedResources field definitions or usage patterns to understand its purpose
rg -n 'ExcludedResources' --type=go | head -20

Repository: migtools/oadp-non-admin

Length of output: 1038


🏁 Script executed:

# Find the definition of alwaysExcludedNamespacedResources and alwaysExcludedClusterResources
rg -n 'alwaysExcluded(Namespaced|Cluster)Resources\s*[=:]' --type=go -A 10

Repository: migtools/oadp-non-admin

Length of output: 1312


🏁 Script executed:

# Check if there are any constants or variables that might contain wildcards
rg -n 'alwaysExcluded' --type=go

Repository: migtools/oadp-non-admin

Length of output: 637


🏁 Script executed:

# Look for Velero API types to understand ExcludedResources field
rg -n 'type.*Spec.*struct' --type=go | grep -i backup | head -5

Repository: migtools/oadp-non-admin

Length of output: 359


🏁 Script executed:

# Check if there are any Velero imports or comments that explain ExcludedResources behavior
rg -n 'velerov1\.(Backup|Restore)' --type=go -B 2 -A 2 | head -40

Repository: migtools/oadp-non-admin

Length of output: 3473


🏁 Script executed:

# Look for test cases that show ExcludedResources behavior with wildcards
rg -n 'ExcludedResources.*\*|"\*".*ExcludedResources' --type=go -B 2 -A 2

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Check if the code has any other wildcard validation patterns
rg -n 'slices.Contains.*\*' --type=go -B 2 -A 2

Repository: migtools/oadp-non-admin

Length of output: 1078


🏁 Script executed:

# Verify the actual risk: check if user can set ExcludedResources to "*"
rg -n 'ExcludedResources\s*:' --type=go | grep -v '//'

Repository: migtools/oadp-non-admin

Length of output: 254


🏁 Script executed:

# Check the actual BackupSpec and RestoreSpec type definitions to confirm ExcludedResources is user-provided
fd -t f -e go -x grep -l 'type.*BackupSpec\|type.*RestoreSpec' {} \; 2>/dev/null | head -10

Repository: migtools/oadp-non-admin

Length of output: 197


🏁 Script executed:

# Verify if there are any tests that show user setting ExcludedResources with "*"
rg -n 'ExcludedResources.*=.*\[\]string' --type=go -A 3 | head -30

Repository: migtools/oadp-non-admin

Length of output: 889


🏁 Script executed:

# Check NonAdminBackupSpec to see if it embeds BackupSpec
sed -n '27,100p' api/v1alpha1/nonadminbackup_types.go

Repository: migtools/oadp-non-admin

Length of output: 2728


🏁 Script executed:

# Check if ExcludedResources is user-configurable by looking at how it's populated
rg -n 'backupSpec.ExcludedResources\s*=' --type=go -B 3 -A 1

Repository: migtools/oadp-non-admin

Length of output: 723


🏁 Script executed:

# Look for where ExcludedResources might be set by users (in API or spec parsing)
rg -n 'ExcludedResources' --type=go | grep -v 'append\|test\|//'

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Check if there's any existing pattern for checking wildcards in ExcludedResources
rg -n 'Contains.*ExcludedResources' --type=go -B 2 -A 2

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Check the actual field definitions in Velero's BackupSpec to understand ExcludedResources
# Since we can't access Velero directly, look for any documentation or comments in this repo
rg -n 'ExcludedResources' --type=go -B 5 | grep -E 'type|struct|comment|//' | head -20

Repository: migtools/oadp-non-admin

Length of output: 166


🏁 Script executed:

# Look at how the backup is created with ExcludedResources to understand the risk
sed -n '697,730p' internal/controller/nonadminbackup_controller.go

Repository: migtools/oadp-non-admin

Length of output: 1645


🏁 Script executed:

# Check if there's any validation or comment about the wildcard behavior being specific to scoped fields
rg -n 'wildcard|Wildcard|\*' --type=go -B 2 -A 2 | grep -A 2 -B 2 'scoped\|Scoped' | head -30

Repository: migtools/oadp-non-admin

Length of output: 2519


🏁 Script executed:

# Verify if the test actually shows the wildcard behavior being a Velero validation issue
sed -n '1549,1575p' internal/controller/nonadminbackup_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 1092


🏁 Script executed:

# Check what validation errors Velero throws when mixing wildcards with specific items
# Look for any error handling or validation in the test files
rg -n 'FailedValidation' --type=go -B 3 -A 3

Repository: migtools/oadp-non-admin

Length of output: 868


🏁 Script executed:

# Understand the user flow: can a user actually set ExcludedResources to "*" in the old-style path?
# Check if there are any examples in tests showing user setting this
rg -n 'BackupSpec.*{' --type=go -A 20 | grep -A 15 'ExcludedResources'

Repository: migtools/oadp-non-admin

Length of output: 156


🏁 Script executed:

# Check if there's any documentation about when ExcludedResources vs scoped resources are used
sed -n '697,710p' internal/controller/nonadminbackup_controller.go

Repository: migtools/oadp-non-admin

Length of output: 787


🏁 Script executed:

# Search for any comments or issues related to the old-style path wildcard handling
rg -n 'old-style|fallback' --type=go -B 3 -A 3 | grep -i 'wildcard\|exclude' -B 3 -A 3

Repository: migtools/oadp-non-admin

Length of output: 724


🏁 Script executed:

# Check if there are tests for the fallback path with ExcludedResources containing wildcards
rg -n 'Should not append default excluded resources' --type=go -A 30 | grep -B 30 'ExcludedResources'

Repository: migtools/oadp-non-admin

Length of output: 49


Add wildcard guard to old-style fallback path and restore controller.

The new-style scoped fields correctly prevent appending specific resources when "*" is present. However, the old-style fallback path (lines 722-725) unconditionally appends resources without checking for "*", which would violate Velero's validation if a user sets ExcludedResources: ["*"]. The same gap exists in nonadminrestore_controller.go (lines 351-352).

Add wildcard checks before appending in both locations:

  1. Fallback path (lines 722-725): Wrap both appends with if !slices.Contains(backupSpec.ExcludedResources, "*")
  2. Restore controller (lines 351-352): Wrap the append with if !slices.Contains(restoreSpec.ExcludedResources, "*")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminbackup_controller.go` around lines 709 - 719, The
fallback path unconditionally appends to old-style ExcludedResources which
breaks validation when a user has ExcludedResources: ["*"]; update the logic in
the non-admin backup and restore controllers to check for the wildcard before
appending: in the backup flow, guard the two append calls with if
!slices.Contains(backupSpec.ExcludedResources, "*") (targeting the block that
currently appends
alwaysExcludedNamespacedResources/alwaysExcludedClusterResources when scoped
fields are absent), and in the restore flow guard the append with if
!slices.Contains(restoreSpec.ExcludedResources, "*") so nothing is appended when
the wildcard already excludes everything.

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Summary by CodeRabbit

  • Bug Fixes

  • Corrected exclusion handling so wildcard "*" exclusions are respected and not combined with specific resource entries for backups and restores, preventing conflicting exclusion behavior.

  • Tests

  • Added test coverage for scenarios where wildcard exclusion is used alongside specific resource inclusions to ensure correct post-action behavior.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/controller/nonadminbackup_controller_test.go (1)

1549-1586: Consider adding a sibling case for namespace wildcard handling.

A second table entry for ExcludedNamespaceScopedResources: []string{"*"} would cover the parallel controller guard and close the remaining branch-level regression gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminbackup_controller_test.go` around lines 1549 -
1586, Add a sibling ginkgo.Entry to the table-based tests (same
nonAdminBackupFullReconcileScenario) that mirrors the existing case using
ExcludedClusterScopedResources: []string{"*"} but instead sets the input spec's
ExcludedNamespaceScopedResources: []string{"*"} and asserts the resulting
VeleroBackup.Spec.ExcludedNamespaceScopedResources remains ["*"] (i.e., defaults
are not appended); this will exercise the namespace-wildcard guard in the
controller and close the branch-level regression gap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/controller/nonadminbackup_controller_test.go`:
- Around line 1549-1586: Add a sibling ginkgo.Entry to the table-based tests
(same nonAdminBackupFullReconcileScenario) that mirrors the existing case using
ExcludedClusterScopedResources: []string{"*"} but instead sets the input spec's
ExcludedNamespaceScopedResources: []string{"*"} and asserts the resulting
VeleroBackup.Spec.ExcludedNamespaceScopedResources remains ["*"] (i.e., defaults
are not appended); this will exercise the namespace-wildcard guard in the
controller and close the branch-level regression gap.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16f5d9fc-4ba2-4880-9887-9b41c8f6fb53

📥 Commits

Reviewing files that changed from the base of the PR and between 8f848f1 and 5262fea.

📒 Files selected for processing (3)
  • internal/controller/nonadminbackup_controller.go
  • internal/controller/nonadminbackup_controller_test.go
  • internal/controller/nonadminrestore_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/nonadminbackup_controller.go

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Summary by CodeRabbit

  • Bug Fixes
  • Wildcard "" exclusions are now respected for backups and restores—controller will not append default exclusions when a wildcard is present (including preserving wildcard cluster-scoped exclusions and avoiding adding "volumesnapshotclasses" when "" is used).
  • Tests
  • Added test coverage for wildcard exclusion scenarios to verify correct post-action status and preserved exclusion lists.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
internal/controller/nonadminbackup_controller_test.go (1)

1549-1586: Add companion wildcard coverage for remaining exclusion paths.

This entry covers the cluster-scoped wildcard well. Consider adding table entries for ExcludedNamespaceScopedResources: []string{"*"} and old-style ExcludedResources: []string{"*"} to lock in all wildcard guards introduced in controller logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminbackup_controller_test.go` around lines 1549 -
1586, Add two new ginkgo.Entry test cases alongside the existing
nonAdminBackupFullReconcileScenario entry: one where
spec.BackupSpec.ExcludedNamespaceScopedResources = []string{"*"} and the
expected status.VeleroBackup.Spec.ExcludedNamespaceScopedResources is also
[]string{"*"} (and include the same default ExcludedNamespaceScopedResources
entries in the controller's created VeleroBackup when applicable), and another
legacy-style case where spec.BackupSpec.ExcludedResources = []string{"*"} with
the expected status.VeleroBackup.Spec.ExcludedResources = []string{"*"}; use the
same nonAdminBackupFullReconcileScenario struct and mirror the Conditions/Phase
fields from the existing entry so the test covers wildcard handling for both
namespace-scoped and legacy exclusion fields.
internal/controller/nonadminrestore_controller.go (1)

352-355: Wildcard guard is correct; add a restore wildcard test to prevent regressions.

The guard is correct and aligns with the backup fix. Please add a NonAdminRestore test case for restoreSpec.ExcludedResources = []string{"*"} to ensure "volumesnapshotclasses" is not appended in that path (current expectations around internal/controller/nonadminrestore_controller_test.go Line 364 still only cover non-wildcard behavior).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminrestore_controller.go` around lines 352 - 355,
Add a unit test that covers the wildcard exclusion path so we don't regress the
behavior in nonadminrestore_controller.go: when restoreSpec.ExcludedResources is
set to []string{"*"} the controller must NOT append "volumesnapshotclasses";
implement a new test case (or extend the existing NonAdminRestore test in
nonadminrestore_controller_test) that sets restoreSpec.ExcludedResources =
[]string{"*"}, invokes the code path that runs the logic around slices.Contains
and appending (the block that checks restoreSpec.ExcludedResources and may
append "volumesnapshotclasses"), and assert that "volumesnapshotclasses" is not
present in the final restoreSpec.ExcludedResources. Ensure the test fails if the
append occurs to prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/controller/nonadminbackup_controller_test.go`:
- Around line 1549-1586: Add two new ginkgo.Entry test cases alongside the
existing nonAdminBackupFullReconcileScenario entry: one where
spec.BackupSpec.ExcludedNamespaceScopedResources = []string{"*"} and the
expected status.VeleroBackup.Spec.ExcludedNamespaceScopedResources is also
[]string{"*"} (and include the same default ExcludedNamespaceScopedResources
entries in the controller's created VeleroBackup when applicable), and another
legacy-style case where spec.BackupSpec.ExcludedResources = []string{"*"} with
the expected status.VeleroBackup.Spec.ExcludedResources = []string{"*"}; use the
same nonAdminBackupFullReconcileScenario struct and mirror the Conditions/Phase
fields from the existing entry so the test covers wildcard handling for both
namespace-scoped and legacy exclusion fields.

In `@internal/controller/nonadminrestore_controller.go`:
- Around line 352-355: Add a unit test that covers the wildcard exclusion path
so we don't regress the behavior in nonadminrestore_controller.go: when
restoreSpec.ExcludedResources is set to []string{"*"} the controller must NOT
append "volumesnapshotclasses"; implement a new test case (or extend the
existing NonAdminRestore test in nonadminrestore_controller_test) that sets
restoreSpec.ExcludedResources = []string{"*"}, invokes the code path that runs
the logic around slices.Contains and appending (the block that checks
restoreSpec.ExcludedResources and may append "volumesnapshotclasses"), and
assert that "volumesnapshotclasses" is not present in the final
restoreSpec.ExcludedResources. Ensure the test fails if the append occurs to
prevent regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb88cfda-ff88-4f9e-b763-10b6b6c8c842

📥 Commits

Reviewing files that changed from the base of the PR and between 5262fea and 41e6385.

📒 Files selected for processing (3)
  • internal/controller/nonadminbackup_controller.go
  • internal/controller/nonadminbackup_controller_test.go
  • internal/controller/nonadminrestore_controller.go

When a user sets excludedClusterScopedResources or
excludedNamespaceScopedResources to '*', skip appending default
resources to avoid Velero FailedValidation.

add VolumeGroupSnapshotLabelKey to enforced backup spec tests
to allow tests passing.

fix lint issues.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Michal Pryc <mpryc@redhat.com>
@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 26, 2026

@mpryc: This pull request references OADP-7562 which is a valid jira issue.

Details

In response to this:

When a user sets excludedClusterScopedResources or excludedNamespaceScopedResources to '*', skip appending default resources to avoid Velero FailedValidation.

Why the changes were made

Fix: OADP-7562

How to test the changes made

Unit test was added to cover bug scenario.

Summary by CodeRabbit

  • Bug Fixes
  • Respect wildcard "*" exclusions for backups and restores—controller no longer appends default exclusions when a wildcard is present.
  • Tests
  • Added/expanded tests for wildcard exclusion scenarios and validation of enforced backup fields.
  • Chores
  • Improved controller reliability and type-safety around status updates and request handling; minor code-style/comment cleanup.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/controller/nonadminbackupstoragelocation_controller.go (1)

649-653: Silent failure on type assertion could mask bugs.

Returning false when the type assertion fails silently completes without making updates. While this prevents panics, it could hide programming errors where the wrong object type is passed. Consider adding a log statement at debug level when the assertion fails.

🔧 Suggestion to add logging on assertion failure
 		if updateErr := r.updateStatusWithRetry(ctx, logger, nabslRequest, func(obj client.Object) bool {
 			req, ok := obj.(*nacv1alpha1.NonAdminBackupStorageLocationRequest)
 			if !ok {
+				// This should never happen - log for debugging if it does
+				logger.V(1).Info("Unexpected: failed to assert object as NonAdminBackupStorageLocationRequest")
 				return false
 			}
 			return updatePhaseIfNeeded(&req.Status.Phase, req.Spec.ApprovalDecision)
 		}); updateErr != nil {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminbackupstoragelocation_controller.go` around lines
649 - 653, The type assertion for obj to
*nacv1alpha1.NonAdminBackupStorageLocationRequest currently returns false
silently on failure; add a debug log when !ok to record the unexpected type
(e.g., log.Debugf or controllerLogger.V(1).Info) including the actual object
type via fmt.Sprintf("%T", obj) and any identifying info, then return false as
before. Update the block around the assertion (the req, ok :=
obj.(*nacv1alpha1.NonAdminBackupStorageLocationRequest) check that then calls
updatePhaseIfNeeded) to emit this debug log before returning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/common/function/function_test.go`:
- Around line 381-385: The test includes a case named
"VolumeGroupSnapshotLabelKey" that references
BackupSpec.VolumeGroupSnapshotLabelKey which does not exist in the vendored
Velero v1.14.0; remove that test case (or replace it with a field present in
v1.14.0) so the completeness check in function_test.go (the reflection-based
validation around lines 460-475) no longer expects the nonexistent field;
alternatively, if you intend to keep the test, upgrade the Velero dependency to
v1.17.0+ so BackupSpec contains VolumeGroupSnapshotLabelKey and the reflection
check will pass.

In `@internal/controller/nonadminrestore_controller.go`:
- Around line 352-355: Add a unit test that exercises the code path which checks
slices.Contains(restoreSpec.ExcludedResources, constant.WildcardString) so we
verify the wildcard case is preserved: create a Restore (or restoreSpec) with
ExcludedResources set to []string{constant.WildcardString}, invoke the
controller logic that runs the exclusion-normalization (the code in
nonadminrestore_controller.go — the Reconcile / processing function that
contains the slices.Contains check), and assert that ExcludedResources still
equals []string{constant.WildcardString} (i.e., "volumesnapshotclasses" is not
appended). Ensure the test uses the same symbols restoreSpec.ExcludedResources
and constant.WildcardString for clarity.

---

Nitpick comments:
In `@internal/controller/nonadminbackupstoragelocation_controller.go`:
- Around line 649-653: The type assertion for obj to
*nacv1alpha1.NonAdminBackupStorageLocationRequest currently returns false
silently on failure; add a debug log when !ok to record the unexpected type
(e.g., log.Debugf or controllerLogger.V(1).Info) including the actual object
type via fmt.Sprintf("%T", obj) and any identifying info, then return false as
before. Update the block around the assertion (the req, ok :=
obj.(*nacv1alpha1.NonAdminBackupStorageLocationRequest) check that then calls
updatePhaseIfNeeded) to emit this debug log before returning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26d8ffa8-a72c-4d48-8e49-cecc5e777810

📥 Commits

Reviewing files that changed from the base of the PR and between 41e6385 and 1719fdd.

📒 Files selected for processing (6)
  • internal/common/constant/constant.go
  • internal/common/function/function_test.go
  • internal/controller/nonadminbackup_controller.go
  • internal/controller/nonadminbackup_controller_test.go
  • internal/controller/nonadminbackupstoragelocation_controller.go
  • internal/controller/nonadminrestore_controller.go
✅ Files skipped from review due to trivial changes (1)
  • internal/common/constant/constant.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/nonadminbackup_controller_test.go

Comment on lines +381 to +385
{
name: "VolumeGroupSnapshotLabelKey",
enforcedValue: "enforced-label-key",
overrideValue: "user-label-key",
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify VolumeGroupSnapshotLabelKey field exists in Velero BackupSpec

# Check go.mod for Velero version
echo "=== Velero version in go.mod ==="
rg -n 'github.com/vmware-tanzu/velero' go.mod

# Search for VolumeGroupSnapshotLabelKey usage in the codebase
echo -e "\n=== VolumeGroupSnapshotLabelKey references ==="
rg -n 'VolumeGroupSnapshotLabelKey'

# Verify the field exists in imported Velero types
echo -e "\n=== BackupSpec struct fields (if vendored) ==="
fd -t f 'backup_types.go' --exec grep -A 100 'type BackupSpec struct' {} \; 2>/dev/null | head -150

Repository: migtools/oadp-non-admin

Length of output: 736


🏁 Script executed:

#!/bin/bash
# Check the BackupSpec struct definition in the project
echo "=== Looking for BackupSpec struct in project imports ==="
rg -n 'type BackupSpec struct' --type go

# Check the imports in function_test.go to see which Velero version is being used
echo -e "\n=== Imports in function_test.go ==="
head -50 internal/common/function/function_test.go | grep -A 20 'import'

# Check the completeness check logic at lines 460-475
echo -e "\n=== Completeness check logic ==="
sed -n '455,480p' internal/common/function/function_test.go

Repository: migtools/oadp-non-admin

Length of output: 1531


🏁 Script executed:

#!/bin/bash
# Check when VolumeGroupSnapshotLabelKey was added to Velero
echo "=== Searching Velero upstream for VolumeGroupSnapshotLabelKey ==="

# Try to find any Velero types file that might be in vendor or cached
fd -t f -name '*velero*' -type f | grep -E '(types|backup)' | head -20

# Check if there's a vendor directory with Velero types
echo -e "\n=== Checking for vendored Velero types ==="
find . -path '*/velero/pkg/apis/velero/v1/*' -name '*.go' 2>/dev/null | head -10

# Look for any BackupSpec references that show actual field names
echo -e "\n=== Looking for BackupSpec field references in YAML/config ==="
rg -A 50 'type.*BackupSpec' --type go 2>/dev/null | head -100

Repository: migtools/oadp-non-admin

Length of output: 4242


🌐 Web query:

Velero VolumeGroupSnapshotLabelKey release version when added

💡 Result:

The VolumeGroupSnapshotLabelKey feature, which allows customizing the label key used by Velero to group PVCs for VolumeGroupSnapshots (default: velero.io/volume-group), was added in Velero v1.17.0. It is configurable via the Backup spec field volumeGroupSnapshotLabelKey, the server flag --volume-group-snapshot-label-key, or defaults to velero.io/volume-group.

Citations:


CRITICAL: Test case references a non-existent BackupSpec field in current Velero version.

The VolumeGroupSnapshotLabelKey field was added in Velero v1.17.0, but this project uses v1.14.0. The test case at lines 381-385 will fail when the completeness check (lines 460-475) attempts to validate the field against the actual velerov1.BackupSpec struct via reflection. The field does not exist in v1.14.0's BackupSpec.

Either upgrade Velero to v1.17.0 or later, or remove this test case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/common/function/function_test.go` around lines 381 - 385, The test
includes a case named "VolumeGroupSnapshotLabelKey" that references
BackupSpec.VolumeGroupSnapshotLabelKey which does not exist in the vendored
Velero v1.14.0; remove that test case (or replace it with a field present in
v1.14.0) so the completeness check in function_test.go (the reflection-based
validation around lines 460-475) no longer expects the nonexistent field;
alternatively, if you intend to keep the test, upgrade the Velero dependency to
v1.17.0+ so BackupSpec contains VolumeGroupSnapshotLabelKey and the reflection
check will pass.

Comment on lines +352 to +355
if !slices.Contains(restoreSpec.ExcludedResources, constant.WildcardString) {
restoreSpec.ExcludedResources = append(restoreSpec.ExcludedResources,
"volumesnapshotclasses")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if there are tests covering the wildcard exclusion scenario for restores

# Search for test cases involving ExcludedResources with wildcard in restore controller tests
rg -n -C5 'ExcludedResources.*\*|"\*"' internal/controller/nonadminrestore_controller_test.go

# Also check if there's any wildcard-related test naming
rg -n 'wildcard|Wildcard' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Check the implementation at lines 352-355
cat -n internal/controller/nonadminrestore_controller.go | sed -n '350,360p'

Repository: migtools/oadp-non-admin

Length of output: 479


🏁 Script executed:

# Check the test file structure and the mentioned lines 369-370
wc -l internal/controller/nonadminrestore_controller_test.go
head -n 1 internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 128


🏁 Script executed:

# Look at the test file around lines 369-370 to see what's being tested
sed -n '365,380p' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 779


🏁 Script executed:

# Search for any test setup with wildcard patterns in ExcludedResources across the entire test file
rg -n 'ExcludedResources' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 143


🏁 Script executed:

# Look for scenario setup that might include wildcard configurations
rg -n 'scenario\.|WildcardString' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 1682


🏁 Script executed:

# Check what WildcardString constant is defined as
rg -n 'WildcardString.*=' internal/pkg/constant/ | head -5

Repository: migtools/oadp-non-admin

Length of output: 129


🏁 Script executed:

# Look for any scenario definitions in the test file to understand test structure
sed -n '1,150p' internal/controller/nonadminrestore_controller_test.go | grep -A2 'type.*scenario\|var.*scenario' | head -20

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Find where WildcardString is defined
rg -n 'WildcardString' --type go | head -10

Repository: migtools/oadp-non-admin

Length of output: 821


🏁 Script executed:

# Look at the test scenarios structure - search for test case definitions
sed -n '80,200p' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 5489


🏁 Script executed:

# Search for test scenario definitions in the test file
rg -n 'nonAdminRestoreFullReconcileScenario\{' internal/controller/nonadminrestore_controller_test.go -A 15 | head -100

Repository: migtools/oadp-non-admin

Length of output: 2266


🏁 Script executed:

# Look for all test entry definitions with Entry() to see if any test the wildcard scenario
rg -n 'ginkgo.Entry' internal/controller/nonadminrestore_controller_test.go -B 2 -A 8 | head -150

Repository: migtools/oadp-non-admin

Length of output: 1917


🏁 Script executed:

# Look at the test scenario structures to see if any include ExcludedResources with wildcard
sed -n '451,610p' internal/controller/nonadminrestore_controller_test.go | grep -C 10 'ExcludedResources'

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Search for all test scenario definitions to find if wildcard is tested
rg -n 'ginkgo.Entry.*"Should' internal/controller/nonadminrestore_controller_test.go

Repository: migtools/oadp-non-admin

Length of output: 638


🏁 Script executed:

# Get more details on what test scenarios exist and their structure
sed -n '451,625p' internal/controller/nonadminrestore_controller_test.go | head -200

Repository: migtools/oadp-non-admin

Length of output: 5120


🏁 Script executed:

# Check if there's any test that explicitly uses wildcard in ExcludedResources for restore tests
rg -n '\*' internal/controller/nonadminrestore_controller_test.go | grep -i 'exclude\|resource' | head -10

Repository: migtools/oadp-non-admin

Length of output: 49


🏁 Script executed:

# Look at the backup controller to see if it has similar tests and if wildcard scenarios are tested there
rg -n 'wildcard\|WildcardString' internal/controller/nonadminbackup_controller_test.go -B 2 -A 2

Repository: migtools/oadp-non-admin

Length of output: 49


Confirm test coverage gap: add test case for wildcard exclusion scenario.

The implementation correctly prevents appending volumesnapshotclasses when "*" is already in ExcludedResources, avoiding Velero validation failures. However, test coverage exists only for the normal case where volumesnapshotclasses is appended (line 369). A test case is missing to verify that when ExcludedResources = ["*"], it remains unchanged after processing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/nonadminrestore_controller.go` around lines 352 - 355,
Add a unit test that exercises the code path which checks
slices.Contains(restoreSpec.ExcludedResources, constant.WildcardString) so we
verify the wildcard case is preserved: create a Restore (or restoreSpec) with
ExcludedResources set to []string{constant.WildcardString}, invoke the
controller logic that runs the exclusion-normalization (the code in
nonadminrestore_controller.go — the Reconcile / processing function that
contains the slices.Contains check), and assert that ExcludedResources still
equals []string{constant.WildcardString} (i.e., "volumesnapshotclasses" is not
appended). Ensure the test uses the same symbols restoreSpec.ExcludedResources
and constant.WildcardString for clarity.

@mpryc
Copy link
Copy Markdown
Collaborator Author

mpryc commented Mar 26, 2026

/unhold

@mpryc
Copy link
Copy Markdown
Collaborator Author

mpryc commented Mar 26, 2026

/hold testing...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants