Skip to content

fix: apply managed-by label for namespaceManagement tenant namespaces…#2120

Open
nmirasch wants to merge 3 commits intoargoproj-labs:masterfrom
nmirasch:GITOPS-9071_tenant_namespace_not_labeled
Open

fix: apply managed-by label for namespaceManagement tenant namespaces…#2120
nmirasch wants to merge 3 commits intoargoproj-labs:masterfrom
nmirasch:GITOPS-9071_tenant_namespace_not_labeled

Conversation

@nmirasch
Copy link
Copy Markdown
Contributor

@nmirasch nmirasch commented Mar 10, 2026

… and fix related bugs (#2039)

What type of PR is this?

/kind bug

What does this PR do / why we need it:
Namespace-scoped Argo CD with spec.namespaceManagement was not labeling the tenant namespace with argocd.argoproj.io/managed-by, so Applications in that namespace were not discovered. This PR fixes that by applying the managed-by label from the role controller when reconciling the application-controller role for managed namespaces
Changes include related fixes (single status update in NamespaceManagement, correct namespace in RBAC skip logic), unit and E2E testing

Which issue(s) this PR fixes:

Fixes #2039?

Summary by CodeRabbit

  • New Features

    • Operator now ensures tenant namespaces receive the ArgoCD managed-by label when relevant, improving multi-namespace behavior.
  • Bug Fixes

    • Consistent RBAC cleanup skipping now uses the ArgoCD instance namespace to identify managed namespaces.
  • Tests

    • Expanded unit and e2e tests validate managed-by labeling, RBAC creation, application discovery, deployment, and cleanup in tenant namespaces.
  • Chores

    • e2e start command exports a flag to enable namespace-management in namespace-scoped runs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3787ca5a-3256-4b44-b04f-438bfdd9e772

📥 Commits

Reviewing files that changed from the base of the PR and between 44c9352 and 3061568.

📒 Files selected for processing (2)
  • controllers/argocd/util.go
  • controllers/argocd/util_test.go

📝 Walkthrough

Walkthrough

Labels tenant Namespaces with the Argo CD managed-by namespace during role reconciliation, changes RBAC-cleanup skip checks to compare the namespace's managed-by label against the ArgoCD instance namespace, adjusts NamespaceManagement reconcile/delete/update handling, adds tests and e2e start env var to enable namespace-scoped runs.

Changes

Cohort / File(s) Summary
Makefile
Makefile
Set ALLOW_NAMESPACE_MANAGEMENT_IN_NAMESPACE_SCOPED_INSTANCES=true in start-e2e target environment.
Controller: RBAC cleanup & skip logic
controllers/argocd/argocd_controller.go, controllers/argocd/namespaceManagement.go
Change RBAC-deletion skip condition to compare namespace argocd.argoproj.io/managed-by label with the ArgoCD instance namespace (argocd.Namespace) and update log messages.
NamespaceManagement reconcile behavior
controllers/argocd/namespaceManagement.go, controllers/argocd/util.go
Stop appending an NM-specific status when an NM CR is not permitted; use tenant Namespace (NM.Namespace) for label checks in update/delete handlers and gate cleanup accordingly.
Role reconciliation / namespace labeling
controllers/argocd/role.go
On reconciling Application Controller roles for non-local namespaces, ensure the tenant Namespace has argocd.argoproj.io/managed-by=<argocd namespace> set (create/patch labels if missing).
Unit tests
controllers/argocd/namespaceManagement_test.go, controllers/argocd/role_test.go, controllers/argocd/util_test.go
Update expectations to use ArgoCD instance namespace for managed-by checks; add tests verifying the managed-by label is added and preserved and that handlers behave with tenant Namespaces present.
Integration tests / fixtures
tests/ginkgo/parallel/1-012_validate-managed-by-chain_test.go
Add helpers to toggle operator env var for NamespaceManagement in tests, extend test context/teardown, and add an e2e validation for managed-by labeling, RBAC creation, and Application discovery in tenant namespaces.

Sequence Diagram

sequenceDiagram
    participant ArgoCD as ArgoCD Instance
    participant NMCtrl as NamespaceManagement Controller
    participant RoleCtrl as Role Reconciler
    participant Namespace as Tenant Namespace
    participant RBAC as RBAC Resources

    ArgoCD->>NMCtrl: spec.namespaceManagement / NamespaceManagement CR
    NMCtrl->>NMCtrl: validate NM targeting this ArgoCD instance
    NMCtrl->>RoleCtrl: trigger role reconciliation for app-controller
    RoleCtrl->>Namespace: Get Namespace object
    alt managed-by label missing or differs
        RoleCtrl->>Namespace: Patch labels -> set managed-by = ArgoCD namespace
        Namespace-->>RoleCtrl: Return updated Namespace
    end
    RoleCtrl->>RBAC: Create/Update Role & RoleBinding in tenant namespace
    RBAC-->>RoleCtrl: Reconciled
    NMCtrl->>RBAC: On disable/cleanup, compare namespace managed-by to ArgoCD namespace and skip deletion if mismatch
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jgwest
  • svghadi
  • anandf

Poem

🐰 I hopped through namespaces, soft and spry,
I stitched a label where Applications lie,
RBAC kept tidy, cleanup checks now true,
Tenant apps find home and say "how-do-you-do",
A tiny rabbit cheers — discoverability! 🎋

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: applying the managed-by label to tenant namespaces in namespaceManagement, which directly addresses the core issue reported.
Linked Issues check ✅ Passed The PR applies the managed-by label when reconciling the application-controller role for managed namespaces [role.go], fixes incorrect namespace references in RBAC skip logic [argocd_controller.go, namespaceManagement.go, util.go], and adds comprehensive unit and E2E tests validating the labeling behavior.
Out of Scope Changes check ✅ Passed All code changes directly support applying the managed-by label and fixing related namespace-scoped namespaceManagement logic. The Makefile e2e flag change enables testing of namespace-scoped instances with the required environment variable.

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

✨ Finishing Touches
🧪 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
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)
controllers/argocd/role.go (1)

118-133: Consider returning errors from label update failures.

The current implementation logs errors but continues reconciliation when:

  1. The namespace cannot be fetched (line 122-123)
  2. The label update fails (line 129-131)

While non-fatal error handling can be intentional (e.g., if the namespace is terminating), silently continuing when the update fails means the label won't be applied until the next reconciliation cycle. This could delay Application discovery in the tenant namespace.

If the label is critical for Application discovery (which is the core fix in this PR), consider propagating the update error to trigger a requeue:

♻️ Optional: Propagate label update error
 		if cr.Namespace != namespace.Name && name == common.ArgoCDApplicationControllerComponent {
 			ns := &corev1.Namespace{}
 			if err := r.Get(context.TODO(), types.NamespacedName{Name: namespace.Name}, ns); err != nil {
 				log.Error(err, "failed to get namespace for managed-by label", "namespace", namespace.Name)
+				// Continue - namespace might be terminating or temporarily unavailable
 			} else if ns.Labels[common.ArgoCDManagedByLabel] != cr.Namespace {
 				if ns.Labels == nil {
 					ns.Labels = make(map[string]string)
 				}
 				ns.Labels[common.ArgoCDManagedByLabel] = cr.Namespace
 				argoutil.LogResourceUpdate(log, ns, fmt.Sprintf("adding label '%s=%s'", common.ArgoCDManagedByLabel, cr.Namespace))
 				if err := r.Update(context.TODO(), ns); err != nil {
-					log.Error(err, fmt.Sprintf("failed to add label to namespace [%s]", namespace.Name))
+					return nil, fmt.Errorf("failed to add managed-by label to namespace [%s]: %w", namespace.Name, err)
 				}
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/argocd/role.go` around lines 118 - 133, The code currently logs
failures from r.Get and r.Update when setting the common.ArgoCDManagedByLabel
but continues reconciliation; change this to propagate those errors so the
caller (Reconcile) will requeue. Specifically, in the block that checks
common.ArgoCDApplicationControllerComponent, replace the log-only paths for
r.Get(context.TODO(), types.NamespacedName{Name: namespace.Name}, ns) and
r.Update(context.TODO(), ns) with returns of the encountered error (or wrap with
context using fmt.Errorf) so the reconcile loop sees the failure; keep
argoutil.LogResourceUpdate and label mutation logic intact but ensure any
Get/Update error is returned from the enclosing reconcile function instead of
just logged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@controllers/argocd/role.go`:
- Around line 118-133: The code currently logs failures from r.Get and r.Update
when setting the common.ArgoCDManagedByLabel but continues reconciliation;
change this to propagate those errors so the caller (Reconcile) will requeue.
Specifically, in the block that checks
common.ArgoCDApplicationControllerComponent, replace the log-only paths for
r.Get(context.TODO(), types.NamespacedName{Name: namespace.Name}, ns) and
r.Update(context.TODO(), ns) with returns of the encountered error (or wrap with
context using fmt.Errorf) so the reconcile loop sees the failure; keep
argoutil.LogResourceUpdate and label mutation logic intact but ensure any
Get/Update error is returned from the enclosing reconcile function instead of
just logged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65ee0521-cb77-48e4-8b45-f915e9761ab7

📥 Commits

Reviewing files that changed from the base of the PR and between cc87fa2 and b5f8d1a.

📒 Files selected for processing (7)
  • Makefile
  • controllers/argocd/argocd_controller.go
  • controllers/argocd/namespaceManagement.go
  • controllers/argocd/namespaceManagement_test.go
  • controllers/argocd/role.go
  • controllers/argocd/role_test.go
  • tests/ginkgo/parallel/1-012_validate-managed-by-chain_test.go

Copy link
Copy Markdown
Contributor

@Mangaal Mangaal left a comment

Choose a reason for hiding this comment

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

@nmirasch, Overall changes look good to me.
I was also testing this out locally.
I found a possible bug within the NamespaceManagement event handler

func (r *ReconcileArgoCD) handleNamespaceManagementUpdate(oldNSMgmt, newNSMgmt *argoproj.NamespaceManagement, k8sClient kubernetes.Interface) bool {
	ns := &corev1.Namespace{}
	if err := r.Get(context.TODO(), types.NamespacedName{Name: oldNSMgmt.Spec.ManagedBy}, ns); err != nil {
		return false
	}

	// Skip Update if managed-by label exists and matches .spec.managedBy
	if labelVal, labelExists := ns.Labels[common.ArgoCDManagedByLabel]; labelExists && labelVal == oldNSMgmt.Spec.ManagedBy {
		log.Info(fmt.Sprintf("Namespace %s still managed by same ArgoCD instance via label, skipping update cleanup", oldNSMgmt.Namespace))
		return false
	}
func (r *ReconcileArgoCD) handleNamespaceManagementDelete(nsMgmt *argoproj.NamespaceManagement, k8sClient kubernetes.Interface) bool {
	ns := &corev1.Namespace{}
	if err := r.Get(context.TODO(), types.NamespacedName{Name: nsMgmt.Spec.ManagedBy}, ns); err != nil {
		return false
	}

	// Skip cleanup if managed-by label exists and matches .spec.managedBy
	if labelVal, labelExists := ns.Labels[common.ArgoCDManagedByLabel]; labelExists && labelVal == nsMgmt.Spec.ManagedBy {
		log.Info(fmt.Sprintf("Namespace %s still managed by same ArgoCD instance via label, skipping delete cleanup", nsMgmt.Namespace))
		return false
	}

In both cases, we fetch the namespace name by .spec.managedBy, which is the Argo CD instance namespace, and then check argocd.argoproj.io/managed-by on that namespace.

I think this should instead fetch the managed tenant namespace:

The managed-by label is expected on the managed namespace, not on the Argo CD instance namespace. This can cause a cleanup that may remove RBAC unexpectedly.

We can include the fix in this pr or have a separate pr for this issue

CC: @svghadi

https://github.com/argoproj-labs/argocd-operator/blob/master/controllers/argocd/util.go#L2080-L2085

… and fix related bugs (argoproj-labs#2039)

Signed-off-by: nmirasch <neus.miras@gmail.com>
Signed-off-by: nmirasch <neus.miras@gmail.com>
…handlers

Signed-off-by: nmirasch <neus.miras@gmail.com>
@nmirasch nmirasch force-pushed the GITOPS-9071_tenant_namespace_not_labeled branch from 3061568 to 2604f18 Compare March 12, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants