Skip to content

Add missing delete permission for APIKeyApproval owner references#64

Merged
R-Lawton merged 2 commits into
mainfrom
missing-permissions
May 21, 2026
Merged

Add missing delete permission for APIKeyApproval owner references#64
R-Lawton merged 2 commits into
mainfrom
missing-permissions

Conversation

@eguzki
Copy link
Copy Markdown
Contributor

@eguzki eguzki commented May 21, 2026

Summary

  • Add delete permission to APIKeyApprovalReconciler RBAC for setting owner references
  • Add e2e test to verify APIKeyApproval garbage collection when APIKey is deleted

Problem

The controller was failing to set owner references on APIKeyApproval resources with the error:

cannot set an ownerRef on a resource you can't delete

Kubernetes requires delete permission on a resource before you can set owner references that enable garbage collection.

Solution

  • Added delete verb to the apikeyapprovals RBAC marker in apikeyapproval_controller.go
  • Regenerated RBAC manifests with make manifests
  • Added e2e test (apikey_approval_gc_test.go) that validates:
    • APIKeyApproval gets owner reference to APIKeyRequest
    • APIKeyApproval is automatically garbage collected when APIKey is deleted

Testing

The new e2e test verifies the complete cleanup chain:

  1. Delete APIKey → APIKeyRequestReconciler deletes APIKeyRequest
  2. Delete APIKeyRequest → Kubernetes GC deletes APIKeyApproval (via owner reference)

Summary by CodeRabbit

Release Notes

  • Tests

    • Added comprehensive end-to-end test suite validating APIKeyApproval garbage collection behaviour, confirming that approval records are automatically cleaned up when their associated API keys are deleted from the system.
  • Chores

    • Updated role-based access control permissions to enable APIKeyApproval deletion operations.

Review Change Stack

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Warning

Rate limit exceeded

@eguzki has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 31 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86399c97-36e6-4f39-93c3-299d3e2057be

📥 Commits

Reviewing files that changed from the base of the PR and between e86d520 and d6a539a.

📒 Files selected for processing (4)
  • test/e2e/apikey_approval_gc_test.go
  • test/e2e/automatic_approval_test.go
  • test/e2e/e2e_test.go
  • test/e2e/test_helpers.go
📝 Walkthrough

Walkthrough

This PR enables APIKeyApproval garbage collection by splitting RBAC rules for apikeyrequests and apiproducts, granting the apikeyapproval controller delete permissions, and validating the complete lifecycle with an end-to-end test that verifies cleanup when API keys are removed.

Changes

APIKeyApproval Garbage Collection and RBAC

Layer / File(s) Summary
RBAC rules split for apikeyrequests and apiproducts
config/rbac/role.yaml
The manager-role ClusterRole RBAC rules for devportal.kuadrant.io apikeyrequests and apiproducts are split into two separate rules: one grants create and delete verbs; another grants get, list, patch, update, and watch. Status resources remain unchanged.
Controller RBAC annotation with delete permission
internal/controller/apikeyapproval_controller.go
The apikeyapproval controller kubebuilder RBAC annotation adds the delete verb to enable garbage collection of APIKeyApproval objects.
End-to-end test for APIKeyApproval garbage collection
test/e2e/apikey_approval_gc_test.go
A comprehensive Ginkgo test suite validates the APIKeyApproval lifecycle: creates multi-namespace test environment with HTTPRoute and AuthPolicy, provisions APIProduct and APIKey, verifies APIKeyRequest and APIKeyApproval creation with correct ownership, and confirms both objects are garbage collected when the APIKey is deleted.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • didierofrivia
  • R-Lawton
  • emmaaroche

Poem

🐰 A rabbit hops through RBAC rules so fine,
Splitting verbs to keep permissions aligned,
The controller deletes with newfound might,
While tests ensure garbage collection's right! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding the missing delete permission for APIKeyApproval to enable owner references and garbage collection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch missing-permissions

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/e2e/apikey_approval_gc_test.go (2)

306-315: 💤 Low value

Brittle assumption: apikeyapproval name hardcoded as <request>-auto.

apiKeyApprovalName is derived purely by string formatting ("%s-auto"). If the controller's naming convention for auto-approved requests is ever adjusted (suffix change, hashing, truncation for length limits, etc.), every subsequent assertion in this spec — owner ref name, kind, controller flag, and GC verification — silently fails for the wrong reason. Consider discovering the approval by querying with a label selector or by listing apikeyapprovals whose owner-reference UID matches the APIKeyRequest, rather than reconstructing the name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/apikey_approval_gc_test.go` around lines 306 - 315, The test
currently hardcodes apiKeyApprovalName via fmt.Sprintf("%s-auto") and then
checks for that exact name in verifyApprovalCreated; change this to discover the
created APIKeyApproval dynamically by listing apikeyapprovals in ownerNamespace
and selecting the one that references the APIKeyRequest (use ownerReference UID
or a stable label on APIKeyRequest) instead of reconstructing the name—replace
the apiKeyApprovalName + verifyApprovalCreated logic with a lookup that runs
kubectl get apikeyapproval -n ownerNamespace and filters by
.metadata.ownerReferences[?(@.uid=="<APIKeyRequest UID>")] (or by label selector
derived from apiKeyRequestName) to read the actual .metadata.name and assert it
exists. Ensure to use the existing apiKeyRequestName and ownerNamespace
variables and propagate the discovered name to subsequent assertions rather than
assuming “-auto”.

50-59: ⚡ Quick win

Early returns in failure-diagnostics hook hide the most useful evidence.

When kubectl get pods fails or returns no controller pod, the hook returns before fetching namespace events. The case where the controller pod is missing is precisely when the events in ownerNamespace/consumerNamespace are most valuable for diagnosing the failure. Log the issue and fall through to the events-gathering blocks instead of returning.

♻️ Fall through to event gathering
-			podOutput, err := utils.Run(cmd)
-			if err != nil {
-				_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get controller pod name: %s\n", err)
-				return
-			}
-			podNames := utils.GetNonEmptyLines(podOutput)
-			if len(podNames) == 0 {
-				_, _ = fmt.Fprintf(GinkgoWriter, "No controller pod found\n")
-				return
-			}
-			controllerPodName := podNames[0]
-
-			By("Fetching controller manager pod logs")
-			cmd = exec.Command("kubectl", "logs", controllerPodName, "-n", controllerNamespace)
-			controllerLogs, err := utils.Run(cmd)
-			if err == nil {
-				_, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n%s\n", controllerLogs)
-			} else {
-				_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s\n", err)
-			}
+			podOutput, err := utils.Run(cmd)
+			var controllerPodName string
+			switch {
+			case err != nil:
+				_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get controller pod name: %s\n", err)
+			default:
+				if podNames := utils.GetNonEmptyLines(podOutput); len(podNames) > 0 {
+					controllerPodName = podNames[0]
+				} else {
+					_, _ = fmt.Fprintf(GinkgoWriter, "No controller pod found\n")
+				}
+			}
+
+			if controllerPodName != "" {
+				By("Fetching controller manager pod logs")
+				cmd = exec.Command("kubectl", "logs", controllerPodName, "-n", controllerNamespace)
+				controllerLogs, err := utils.Run(cmd)
+				if err == nil {
+					_, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n%s\n", controllerLogs)
+				} else {
+					_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s\n", err)
+				}
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/apikey_approval_gc_test.go` around lines 50 - 59, The current
failure-diagnostics early-returns hide important namespace events: when
utils.Run(cmd) returns an error or podNames is empty in the block around
podOutput/podNames, change the logic to log the error/message to GinkgoWriter
(using the existing fmt.Fprintf calls) but do not return; instead let execution
fall through to the subsequent ownerNamespace/consumerNamespace events-gathering
blocks so those events are always collected even if utils.Run(cmd) failed or
podNames is empty. Ensure you reference and preserve the existing variables
(podOutput, err, podNames) and logging calls to avoid losing context while
removing the early return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/e2e/apikey_approval_gc_test.go`:
- Around line 137-138: SetDefaultEventuallyTimeout and
SetDefaultEventuallyPollingInterval are being called at tree-construction time
(top-level in the Describe) and thus mutate Gomega globals for the entire test
binary; move those calls into a per-spec setup (e.g., inside the Describe's
BeforeAll or BeforeEach) so the timeout/polling changes are scoped to this
suite, or alternatively remove the global calls and pass the timeout and
interval explicitly to each Eventually invocation (the test already uses
explicit timeouts in some Eventually calls) — update the code that currently
calls SetDefaultEventuallyTimeout and SetDefaultEventuallyPollingInterval to
instead run inside BeforeAll/BeforeEach or switch to per-Eventually
timeout/interval arguments.

---

Nitpick comments:
In `@test/e2e/apikey_approval_gc_test.go`:
- Around line 306-315: The test currently hardcodes apiKeyApprovalName via
fmt.Sprintf("%s-auto") and then checks for that exact name in
verifyApprovalCreated; change this to discover the created APIKeyApproval
dynamically by listing apikeyapprovals in ownerNamespace and selecting the one
that references the APIKeyRequest (use ownerReference UID or a stable label on
APIKeyRequest) instead of reconstructing the name—replace the apiKeyApprovalName
+ verifyApprovalCreated logic with a lookup that runs kubectl get apikeyapproval
-n ownerNamespace and filters by
.metadata.ownerReferences[?(@.uid=="<APIKeyRequest UID>")] (or by label selector
derived from apiKeyRequestName) to read the actual .metadata.name and assert it
exists. Ensure to use the existing apiKeyRequestName and ownerNamespace
variables and propagate the discovered name to subsequent assertions rather than
assuming “-auto”.
- Around line 50-59: The current failure-diagnostics early-returns hide
important namespace events: when utils.Run(cmd) returns an error or podNames is
empty in the block around podOutput/podNames, change the logic to log the
error/message to GinkgoWriter (using the existing fmt.Fprintf calls) but do not
return; instead let execution fall through to the subsequent
ownerNamespace/consumerNamespace events-gathering blocks so those events are
always collected even if utils.Run(cmd) failed or podNames is empty. Ensure you
reference and preserve the existing variables (podOutput, err, podNames) and
logging calls to avoid losing context while removing the early return 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8703ef76-85c4-410f-bcb7-73cbcbfdd590

📥 Commits

Reviewing files that changed from the base of the PR and between 4058346 and e86d520.

📒 Files selected for processing (3)
  • config/rbac/role.yaml
  • internal/controller/apikeyapproval_controller.go
  • test/e2e/apikey_approval_gc_test.go

Comment thread test/e2e/apikey_approval_gc_test.go Outdated
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@R-Lawton
Copy link
Copy Markdown
Contributor

👀

@R-Lawton R-Lawton merged commit 1bb2539 into main May 21, 2026
18 checks passed
@R-Lawton R-Lawton deleted the missing-permissions branch May 21, 2026 15:58
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.

2 participants