Skip to content

Conversation

@nmirasch
Copy link
Contributor

@nmirasch nmirasch commented Dec 17, 2025

What type of PR is this?

/kind enhancement

What does this PR do / why we need it:
Adds wildcard pattern support (e.g., team-*) to .spec.applicationSet.sourceNamespaces in the ArgoCD Operator. The Operator automatically provisions RBAC permissions (Role and RoleBinding) for the argocd-applicationset-controller ServiceAccount in all namespaces that match the pattern.
Changes Added:

  • Updated getApplicationSetSourceNamespaces() to expand wildcard patterns using glob matching
  • Modified reconciliation logic to process expanded namespaces instead of raw patterns
  • Updated cleanup logic to use pattern matching for resource removal
  • Enhanced deployment command generation to use expanded namespaces
  • Added comprehensive Ginkgo E2E tests covering pattern matching, RBAC provisioning, dynamic namespace handling, and cleanup scenarios.
  • Modified related documentation

Have you updated the necessary documentation?

  • [X ] Documentation update is required by this PR.
  • [X ] Documentation has been updated.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/GITOPS-8278, https://issues.redhat.com/browse/GITOPS-8279

How to test changes / Special notes to the reviewer:

  1. Create test namespaces
    kubectl create namespace team-1 team-2 team-frontend team-backend other-ns

  2. Create ArgoCD with wildcard pattern

apiVersion: argoproj.io/v1beta1
kind: ArgoCD
metadata:
  name: wildcard-example
  namespace: argocd
spec:
  sourceNamespaces:
    - team-*
  applicationSet:
    enabled: true
    sourceNamespaces:
      - team-*
  1. Verify RBAC resources created.
  • Should exist in matching namespaces
    kubectl get role wildcard-example-argocd-applicationset -n team-1
    kubectl get rolebinding wildcard-example-argocd-applicationset -n team-1
  • Should NOT exist in non-matching namespace
    kubectl get role wildcard-example-argocd-applicationset -n other-ns
  1. Test dynamic provisioning
    kubectl create namespace team-3 # Wait ~60 seconds, then verify resources are automatically created
    kubectl get role wildcard-example-argocd-applicationset -n team-3

  2. Verify deployment command
    kubectl get deployment wildcard-example-applicationset-controller -n argocd \ -o jsonpath='{.spec.template.spec.containers[0].command[]}' | \ grep -o '--applicationset-namespaces [^ ]'# Should include: team-1,team-2,team-frontend,team-backend

  3. Test cleanup
    kubectl patch argocd wildcard-example -n argocd --type merge \ -p '{"spec":{"applicationSet":{"sourceNamespaces":["team-1"]}}}'# Wait ~60 seconds, then verify team-2, team-3, etc. have resources removed

Summary by CodeRabbit

  • New Features

    • Wildcard pattern support for ApplicationSet source namespaces (e.g., team-*, *) enabling dynamic namespace targeting and automatic RBAC provisioning for matching namespaces.
  • Documentation

    • Updated guides demonstrating wildcard pattern usage and configuration for ApplicationSet sourceNamespaces across single, multiple, and all namespaces.
  • Tests

    • Enhanced end-to-end test coverage for wildcard namespace matching, RBAC resource provisioning, and pattern updates.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

Walkthrough

This pull request introduces wildcard pattern matching for ApplicationSet source namespaces in the ArgoCD operator. The getApplicationSetSourceNamespaces function now returns ([]string, error) and dynamically resolves glob patterns against actual cluster namespaces. Related code paths in reconciliation, RBAC provisioning, and command construction have been updated to use the expanded namespace list, with comprehensive test coverage and documentation.

Changes

Cohort / File(s) Summary
Core ApplicationSet namespace resolution
controllers/argocd/applicationset.go
Function signature changed from getApplicationSetSourceNamespaces(cr) []string to getApplicationSetSourceNamespaces(cr) ([]string, error). Now performs live namespace listing with glob pattern matching against cr.Spec.ApplicationSet.SourceNamespaces. Updated getArgoApplicationSetCommand to conditionally append --applicationset-namespaces only when matches exist. Reconciliation paths refactored to use expanded, validated namespace sets.
RBAC provisioning guard
controllers/argocd/role.go
Added safeguard in reconcileRoleForApplicationSourceNamespaces to fetch ApplicationSet source namespaces and only append policy rules if the current namespace is in the resolved list and no error occurs.
Unit tests
controllers/argocd/applicationset_test.go
Updated test expectations for namespace ordering and error handling. Added test scaffolding for namespace objects and test cases validating wildcard patterns (e.g., "team-*" matching "team-1", "team-2"). Enhanced getApplicationSetSourceNamespaces assertions to verify error returns and sorted slice comparisons.
Documentation
docs/usage/appsets-in-any-namespace.md
Added sections explaining wildcard support in .spec.applicationSet.sourceNamespaces, with examples for explicit namespace names, glob patterns, and universal wildcard (*). Clarified RBAC provisioning for existing and future matching namespaces.
End-to-end tests
tests/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go
Added comprehensive test validating wildcard namespace matching, including: creation of multiple namespaces with ArgoCD CR patterns, verification of appset deployment command and RBAC resources in matching namespaces, namespace labeling, dynamic namespace creation with automatic provisioning, and narrowing/cleanup of resources when patterns are updated.

Sequence Diagram(s)

sequenceDiagram
    participant Reconciler as ReconcileArgoCD
    participant Client as K8s Client
    participant CR as ApplicationSet<br/>CR Spec
    participant Match as Pattern Matcher
    participant Deploy as Deployment Builder
    participant RBAC as RBAC Provisioner

    Reconciler->>CR: Read sourceNamespaces pattern<br/>(e.g., "team-*")
    Reconciler->>Client: List all Namespaces
    Client-->>Reconciler: [ns1, ns2, ..., team-1, team-2, ...]
    Reconciler->>Match: Filter namespaces against pattern
    Match-->>Reconciler: [team-1, team-2]
    
    alt Namespaces resolved successfully
        Reconciler->>Deploy: Build appset command with<br/>--applicationset-namespaces
        Deploy-->>Reconciler: Deployment with matched namespaces
        
        Reconciler->>RBAC: For each matched namespace,<br/>provision Role + RoleBinding
        RBAC-->>Reconciler: RBAC resources created
    else Error during resolution
        Reconciler->>Reconciler: Log error & handle gracefully
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Function signature propagation: Verify all call sites of getApplicationSetSourceNamespaces properly handle the new error return value and any error-handling logic changes.
  • Glob pattern matching correctness: Review the wildcard/glob matching implementation against test cases (e.g., "team-*" matching), including edge cases like leading/trailing wildcards.
  • Error handling in reconciliation paths: Ensure error handling in reconcileApplicationSetSourceNamespacesResources and removeUnmanagedApplicationSetSourceNamespaceResources properly gates namespace processing and cleanup.
  • RBAC provisioning guard: Verify that the safeguard in reconcileRoleForApplicationSourceNamespaces correctly prevents provisioning when namespaces are not in the resolved list.
  • Test completeness: Confirm E2E test coverage for dynamic namespace creation matching wildcards and cleanup when patterns narrow.

Possibly related PRs

Suggested reviewers

  • jgwest
  • svghadi

Poem

🐰 A rabbit's hop through namespaces wide,
With wildcards dancing—no need to hide,
team-* blooms bright where patterns align,
Namespaces sorted, all clean and fine!
Glob matching magic, so deftly done—
ApplicationSets for everyone!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Wildcards support in ApplicationSet Source Namespaces' accurately describes the main change: introducing wildcard pattern matching capability for ApplicationSet source namespaces.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@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)
controllers/argocd/role.go (1)

264-271: Error handling silently skips appset permissions on failure.

When getApplicationSetSourceNamespaces returns an error, the code silently skips adding ApplicationSet policy rules. While this is a fail-safe approach (doesn't grant extra permissions on error), consider logging the error for debugging purposes:

-		appsetSourceNamespaces, err := r.getApplicationSetSourceNamespaces(cr)
-		if err == nil && contains(appsetSourceNamespaces, sourceNamespace) {
+		appsetSourceNamespaces, appsetErr := r.getApplicationSetSourceNamespaces(cr)
+		if appsetErr != nil {
+			log.Error(appsetErr, "failed to get ApplicationSet source namespaces, skipping appset rules")
+		} else if contains(appsetSourceNamespaces, sourceNamespace) {
 			role.Rules = append(role.Rules, policyRuleForServerApplicationSetSourceNamespaces()...)
 		}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8017fea and 96e5d41.

📒 Files selected for processing (5)
  • controllers/argocd/applicationset.go (5 hunks)
  • controllers/argocd/applicationset_test.go (3 hunks)
  • controllers/argocd/role.go (1 hunks)
  • docs/usage/appsets-in-any-namespace.md (2 hunks)
  • tests/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
controllers/argocd/applicationset.go (3)
controllers/argocd/argocd_controller.go (1)
  • ReconcileArgoCD (73-96)
api/v1alpha1/argocd_types.go (1)
  • ArgoCD (61-67)
api/v1beta1/argocd_types.go (1)
  • ArgoCD (61-67)
controllers/argocd/applicationset_test.go (2)
api/v1alpha1/argocd_types.go (1)
  • ArgoCDApplicationSet (138-166)
api/v1beta1/argocd_types.go (1)
  • ArgoCDApplicationSet (171-223)
tests/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go (4)
tests/ginkgo/fixture/fixture.go (1)
  • OutputDebugOnFail (491-588)
tests/ginkgo/fixture/k8s/fixture.go (3)
  • ExistByName (103-120)
  • NotExistByName (124-143)
  • Update (146-163)
tests/ginkgo/fixture/namespace/fixture.go (2)
  • HaveLabel (27-32)
  • Update (35-51)
tests/ginkgo/fixture/role/fixture.go (1)
  • Update (19-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-operator
  • GitHub Check: Run golangci-lint and gosec
  • GitHub Check: Run end-to-end tests (v1.27.1)
  • GitHub Check: build
🔇 Additional comments (10)
docs/usage/appsets-in-any-namespace.md (1)

17-63: Documentation updates look good.

The documentation clearly explains the three patterns for configuring ApplicationSet source namespaces:

  1. Specific namespace names
  2. Glob patterns (e.g., team-*)
  3. All namespaces wildcard (*)

The examples are well-structured and align with the implementation changes.

controllers/argocd/applicationset_test.go (3)

560-560: Command namespace order changed to alphabetical.

The expected namespace order changed from "foo,bar" to "bar,foo". This reflects the new behavior where namespaces are now listed alphabetically after expansion.


1261-1300: Good test coverage for wildcard pattern matching.

The new test case for wildcard patterns properly validates:

  • Pattern team-* matches team-1 and team-2
  • Non-matching namespace other-ns is correctly excluded
  • Namespace objects are now properly attached to the fake client

1320-1334: Robust test assertions with proper nil handling and sorting.

The updated assertions correctly:

  • Handle the new error return value
  • Normalize nil slices to empty slices for comparison
  • Sort both expected and actual slices to handle nondeterministic ordering from namespace listing
tests/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go (2)

49-50: Debug output expanded for new test namespaces.

Good addition of the new team-* namespaces to the debug output for easier troubleshooting of test failures.


739-972: Comprehensive E2E test for wildcard pattern matching.

This test thoroughly validates the wildcard functionality:

  1. Creates multiple namespaces with team-* pattern and one non-matching
  2. Verifies RBAC resources in all matching namespaces
  3. Confirms non-matching namespace is excluded
  4. Tests dynamic provisioning when new matching namespace is created
  5. Verifies cleanup when pattern is narrowed

The test structure follows the existing patterns in the file and provides good coverage of edge cases.

controllers/argocd/applicationset.go (4)

75-100: Robust namespace expansion with proper error handling.

The implementation correctly:

  • Expands wildcard patterns using getApplicationSetSourceNamespaces
  • Filters expanded namespaces to only those also in apps source namespaces
  • Logs skipped namespaces at debug level (V(1))
  • Only adds --applicationset-namespaces when there are valid namespaces
  • Disables SCM providers only when appset namespaces exist

The nested error handling ensures the command construction continues even if namespace resolution fails.


1001-1012: Cleanup logic correctly uses pattern matching against raw patterns.

The removeUnmanagedApplicationSetSourceNamespaceResources function correctly uses glob.MatchStringInList against the raw cr.Spec.ApplicationSet.SourceNamespaces patterns (not expanded namespaces). This ensures that:

  • Namespaces matching current patterns are kept
  • Namespaces no longer matching are cleaned up

This is the correct approach for cleanup logic since we want to check if a namespace still matches any configured pattern.


662-674: Proper error handling with wrapped errors.

Good use of fmt.Errorf with %w verb for error wrapping, providing proper error context for debugging.


35-36: New dependency on argo-cd v3 glob package.

The import of github.com/argoproj/argo-cd/v3/util/glob is used for glob pattern matching with glob.MatchStringInList, providing consistent glob matching behavior aligned with ArgoCD. This is a reasonable and reliable dependency choice for namespace filtering logic.

Comment on lines +1184 to 1205
// getApplicationSetSourceNamespaces returns the list of actual namespaces that match the patterns
// specified in .spec.ApplicationSet.SourceNamespaces. It supports wildcard patterns (e.g., team-*).
func (r *ReconcileArgoCD) getApplicationSetSourceNamespaces(cr *argoproj.ArgoCD) ([]string, error) {
if cr.Spec.ApplicationSet == nil {
return []string(nil), nil
}
return []string(nil)

sourceNamespaces := []string{}
namespaces := &corev1.NamespaceList{}

if err := r.List(context.TODO(), namespaces, &client.ListOptions{}); err != nil {
return nil, err
}

for _, namespace := range namespaces.Items {
if glob.MatchStringInList(cr.Spec.ApplicationSet.SourceNamespaces, namespace.Name, glob.REGEXP) {
sourceNamespaces = append(sourceNamespaces, namespace.Name)
}
}

return sourceNamespaces, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for applicationset-namespaces usage
rg -n "applicationset-namespaces" --type go -A3 -B3

Repository: argoproj-labs/argocd-operator

Length of output: 9838


🏁 Script executed:

#!/bin/bash
# Check the context around line 90 in applicationset.go
sed -n '60,110p' controllers/argocd/applicationset.go

Repository: argoproj-labs/argocd-operator

Length of output: 2209


🏁 Script executed:

#!/bin/bash
# Check the getApplicationSetSourceNamespaces function implementation
sed -n '1184,1210p' controllers/argocd/applicationset.go

Repository: argoproj-labs/argocd-operator

Length of output: 867


🏁 Script executed:

#!/bin/bash
# Check the test to understand what it expects for namespace ordering
sed -n '540,580p' controllers/argocd/applicationset_test.go

Repository: argoproj-labs/argocd-operator

Length of output: 1341


🏁 Script executed:

#!/bin/bash
# Search for the test setup to see how the mock is configured
sed -n '500,550p' controllers/argocd/applicationset_test.go

Repository: argoproj-labs/argocd-operator

Length of output: 1592


🏁 Script executed:

#!/bin/bash
# Search for the test implementation that checks the command
grep -n "TestReconcileApplicationSet_Deployments_Command" -A 200 controllers/argocd/applicationset_test.go | head -250

Repository: argoproj-labs/argocd-operator

Length of output: 7271


🏁 Script executed:

#!/bin/bash
# Search for any sorting logic in applicationset.go
rg -n "sort" controllers/argocd/applicationset.go -i

Repository: argoproj-labs/argocd-operator

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Check if the fake client sorts results - look at the test setup and fake client
rg -n "makeTestReconcilerClient" controllers/argocd/applicationset_test.go -A 5

Repository: argoproj-labs/argocd-operator

Length of output: 5541


🏁 Script executed:

#!/bin/bash
# Find the definition of makeTestReconcilerClient
grep -n "func makeTestReconcilerClient" controllers/argocd/applicationset_test.go -A 20

Repository: argoproj-labs/argocd-operator

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Search for makeTestReconcilerClient definition in all test files
rg -n "func makeTestReconcilerClient" --type go

Repository: argoproj-labs/argocd-operator

Length of output: 723


🏁 Script executed:

#!/bin/bash
# Check the makeTestReconcilerClient implementation
sed -n '68,100p' controllers/argocd/testing.go

Repository: argoproj-labs/argocd-operator

Length of output: 909


🏁 Script executed:

#!/bin/bash
# Check if the fake client sorts - let's also look for any sorting in the broader context
# Let's check if there's any sort.Strings call or similar
rg -n "sort\|Sort" controllers/argocd/applicationset.go -C 3

Repository: argoproj-labs/argocd-operator

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Let me trace the entire flow - check what order namespaces are returned from the fake client
# by looking at the test more carefully. Let's see if there's any sorting in between
grep -n "contains" controllers/argocd/applicationset.go -B 5 -A 5

Repository: argoproj-labs/argocd-operator

Length of output: 1540


🏁 Script executed:

#!/bin/bash
# Check if fake client implementation sorts - look for controllerruntime or similar
# Let's search for where namespaces might be sorted in the entire codebase
rg -n "sort\.Strings\|strings\.Join.*sort" --type go controllers/

Repository: argoproj-labs/argocd-operator

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Let's check the fake client's List implementation to see if it sorts
# First, let's understand: does the test pass with "bar,foo"?
# Let's look at what the fake client does
rg -n "fake.NewClientBuilder\|controllerruntime.*fake" --type go -B 3 -A 3 | head -50

Repository: argoproj-labs/argocd-operator

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Let's look more carefully at getApplicationSetSourceNamespaces and check if namespaces.Items might be pre-sorted
# Check the full function again and see if there's any processing after the loop
sed -n '1180,1220p' controllers/argocd/applicationset.go

Repository: argoproj-labs/argocd-operator

Length of output: 883


Add sorting to ensure deterministic namespace ordering.

The function returns namespaces in the order they are returned by the Kubernetes API, which is not guaranteed to be deterministic. This can cause the --applicationset-namespaces command argument to differ between reconciliations, triggering unnecessary deployment updates.

Sort the sourceNamespaces slice before returning it to ensure consistent ordering across deployments.

🤖 Prompt for AI Agents
In controllers/argocd/applicationset.go around lines 1184-1205 the returned
sourceNamespaces slice is based on the non-deterministic order from the
Kubernetes API; sort the sourceNamespaces slice (e.g., with sort.Strings) before
returning to ensure deterministic ordering, and add the necessary "sort" import
to the file.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant