Skip to content
This repository was archived by the owner on Sep 9, 2026. It is now read-only.

MGMT-23763: Block IP pool deletion when allocated PublicIPs exist - #456

Merged
SiddarthR56 merged 2 commits into
osac-project:mainfrom
SiddarthR56:MGMT-23763
Apr 30, 2026
Merged

SiddarthR56 merged 2 commits into
osac-project:mainfrom
SiddarthR56:MGMT-23763

Conversation

@SiddarthR56

@SiddarthR56 SiddarthR56 commented Apr 27, 2026 •

Copy link
Copy Markdown
Contributor

PrivatePublicIPPoolsServer.Delete now checks for allocated PublicIP objects before proceeding. If any PublicIP references the pool via spec.pool, the call is rejected with FailedPrecondition including the pool ID and remaining IP count.

Added two unit tests cover the happy and rejection paths (both pass), Manully tested the below 4 scenarios:

  1. Create pool, delete immediately (no IPs) → 200 OK, deletionTimestamp set
  2. Allocate 2 IPs from pool, attempt delete → FailedPrecondition: cannot delete public IP pool '…': 2 public IP(s) are still allocated from it
  3. Delete both IPs, retry pool delete → 200 OK
  4. IPs on pool-B don't block pool-A delete; pool-B still blocked

Summary by CodeRabbit

  • Bug Fixes

    • Prevents deletion of IP pools that still have public IPs allocated; deletion now fails with an explicit error if allocations exist. Lookup failures are surfaced as errors to avoid unintended deletions.
  • Tests

    • Added tests covering deletion rejection when a pool has allocated public IPs and successful deletion when unreferenced.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026 •

Copy link
Copy Markdown

Walkthrough

Build() now instantiates and stores a PublicIP DAO on the PrivatePublicIPPoolsServer. Delete() invokes a new private method that queries PublicIP records filtered by the pool ID (limit 1) and, if any allocated IPs exist, returns a gRPC FailedPrecondition error; if the DAO list call fails it is logged and mapped to a gRPC Internal error. Tests were extended to seed PublicIP records and verify deletion is rejected when allocations exist and allowed when none exist.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • adriengentil
  • eranco74
  • jhernand
🚥 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 captures the main change: adding a check to block deletion of IP pools when allocated PublicIPs still reference them.
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

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/servers/private_public_ip_pools_server.go`:
- Around line 174-182: The referential-integrity guard currently logs lookup
errors and returns nil, allowing deletion to proceed; change the behavior so
lookup failures block deletion by returning the error (or a wrapped error)
instead of nil. In the code block using s.logger.WarnContext with
slog.String("pool_id", poolID) and slog.Any("error", err), replace the final
"return nil" with "return err" (or fmt.Errorf/wrap) so the caller sees the
failure and deletion is aborted; keep the warning log but ensure the method (the
private_public_ip_pools_server delete/verify function) propagates the error.
🪄 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: 32e43c8d-f13d-46e4-be6f-0d6155c8ad6a

📥 Commits

Reviewing files that changed from the base of the PR and between 7bc155e and 0259e39.

📒 Files selected for processing (2)
  • internal/servers/private_public_ip_pools_server.go
  • internal/servers/private_public_ip_pools_server_test.go

Comment thread internal/servers/private_public_ip_pools_server.go
@akshaynadkarni

Copy link
Copy Markdown
Contributor

@SiddarthR56 Can you please add a PR description and capture what tests you ran with the results?

@jhernand

jhernand commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

I think this kind of validation should go in the private API server, not the public one. Otherwise admin users will be able to bypass it via the private API, and potentially break the system. It is already in the private server, sorry.

@akshaynadkarni akshaynadkarni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review comment on error handling approach.


generic, err := NewGenericServer[*privatev1.PublicIPPool]().
SetLogger(b.logger).
SetService(privatev1.PublicIPPools_ServiceDesc.ServiceName).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the soft-fail here intentional?

Currently if the DAO query errors (e.g., DB connectivity issue), pool deletion proceeds anyway (return nil), which could orphan allocated PublicIPs.

An alternative is hard-fail, returning the error so the delete is blocked until the check can actually run:

if err \!= nil {
    return grpcstatus.Errorf(grpccodes.Internal,
        "failed to verify allocated public IPs for pool '%s': %v", poolID, err)
}

For referential integrity checks, hard-fail is usually safer since it prevents data inconsistency at the cost of blocking deletion during transient failures. Curious about the reasoning if soft-fail is intentional here.

@akshaynadkarni akshaynadkarni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please check the comment and add a PR description containing test output.

@SiddarthR56 SiddarthR56 changed the title Block IP pool deletion when allocated PublicIPs exist MGMT-23763: Block IP pool deletion when allocated PublicIPs exist Apr 29, 2026
@openshift-ci-robot

openshift-ci-robot commented Apr 29, 2026 •

Copy link
Copy Markdown

@SiddarthR56: This pull request references MGMT-23763 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the sub-task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

PrivatePublicIPPoolsServer.Delete now checks for allocated PublicIP objects before proceeding. If any PublicIP references the pool via spec.pool, the call is rejected with FailedPrecondition including the pool ID and remaining IP count.
Added two unit tests cover the happy and rejection paths (both pass), Manully tested the below 4 scenarios:

  1. Create pool, delete immediately (no IPs) → 200 OK, deletionTimestamp set
  2. Allocate 2 IPs from pool, attempt delete → FailedPrecondition: cannot delete public IP pool '…': 2 public IP(s) are still allocated from it
  3. Delete both IPs, retry pool delete → 200 OK
  4. IPs on pool-B don't block pool-A delete; pool-B still blocked

Summary by CodeRabbit

Bug Fixes

  • Added validation to prevent deletion of IP pools that have public IPs allocated to them. The system now returns an error if public IPs are still assigned to a pool, protecting against unintended data loss.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
internal/servers/private_public_ip_pools_server_test.go (1)

315-320: Strengthen rejection assertions to validate gRPC contract, not just message substring.

Line 319 currently checks only err.Error() text. Please also assert FailedPrecondition and include checks for pool ID / count so the test protects the API contract.

Proposed test assertion hardening
 import (
 	"context"
 	"fmt"
 
 	"github.com/jackc/pgx/v5/pgxpool"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+	grpccodes "google.golang.org/grpc/codes"
+	grpcstatus "google.golang.org/grpc/status"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/types/known/fieldmaskpb"
@@
 			_, err = poolsServer.Delete(ctx, privatev1.PublicIPPoolsDeleteRequest_builder{
 				Id: poolID,
 			}.Build())
 			Expect(err).To(HaveOccurred())
-			Expect(err.Error()).To(ContainSubstring("public IP(s) are still allocated"))
+			st, ok := grpcstatus.FromError(err)
+			Expect(ok).To(BeTrue())
+			Expect(st.Code()).To(Equal(grpccodes.FailedPrecondition))
+			Expect(st.Message()).To(ContainSubstring(poolID))
+			Expect(st.Message()).To(ContainSubstring("public IP(s) are still allocated"))
 		})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/servers/private_public_ip_pools_server_test.go` around lines 315 -
320, The test currently only checks the error string after calling
poolsServer.Delete with publicv1.PublicIPPoolsDeleteRequest_builder; update the
assertions to validate the gRPC contract by converting err to a gstatus (using
status.FromError) and assert the Code() equals codes.FailedPrecondition, then
also assert the returned error details or message contains the specific poolID
and the allocation count (e.g., verify the error message or details includes
poolID and number of allocated IPs) so the test asserts both the status code and
that the pool identifier/count are reported by Delete.
internal/servers/private_public_ip_pools_server.go (1)

157-162: Avoid fail-open behavior when publicIPDAO is unexpectedly nil.

Line 157-Line 162 skips integrity checks if publicIPDAO is nil. For delete safety, this should fail closed with Internal instead of proceeding.

Proposed fail-closed guard
 func (s *PrivatePublicIPPoolsServer) Delete(ctx context.Context,
 	request *privatev1.PublicIPPoolsDeleteRequest) (response *privatev1.PublicIPPoolsDeleteResponse, err error) {
-	if s.publicIPDAO != nil {
-		err = s.checkNoAllocatedIPs(ctx, request.GetId())
-		if err != nil {
-			return
-		}
-	}
+	if s.publicIPDAO == nil {
+		err = grpcstatus.Error(
+			grpccodes.Internal,
+			"public IP DAO is not configured",
+		)
+		return
+	}
+	err = s.checkNoAllocatedIPs(ctx, request.GetId())
+	if err != nil {
+		return
+	}
 	err = s.generic.Delete(ctx, request, &response)
 	return
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/servers/private_public_ip_pools_server.go` around lines 157 - 162,
The current code skips deletion integrity checks when s.publicIPDAO is nil,
causing a fail-open; change the guard so that if s.publicIPDAO == nil you return
a failing Internal gRPC error instead of proceeding. Specifically, in the method
containing the s.publicIPDAO block, replace the silent no-op path with an
explicit error return (use the gRPC/internal error type your codebase uses)
explaining that publicIPDAO is unexpectedly nil; keep the existing call to
s.checkNoAllocatedIPs(ctx, request.GetId()) when publicIPDAO is present.
🤖 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/servers/private_public_ip_pools_server_test.go`:
- Around line 315-320: The test currently only checks the error string after
calling poolsServer.Delete with publicv1.PublicIPPoolsDeleteRequest_builder;
update the assertions to validate the gRPC contract by converting err to a
gstatus (using status.FromError) and assert the Code() equals
codes.FailedPrecondition, then also assert the returned error details or message
contains the specific poolID and the allocation count (e.g., verify the error
message or details includes poolID and number of allocated IPs) so the test
asserts both the status code and that the pool identifier/count are reported by
Delete.

In `@internal/servers/private_public_ip_pools_server.go`:
- Around line 157-162: The current code skips deletion integrity checks when
s.publicIPDAO is nil, causing a fail-open; change the guard so that if
s.publicIPDAO == nil you return a failing Internal gRPC error instead of
proceeding. Specifically, in the method containing the s.publicIPDAO block,
replace the silent no-op path with an explicit error return (use the
gRPC/internal error type your codebase uses) explaining that publicIPDAO is
unexpectedly nil; keep the existing call to s.checkNoAllocatedIPs(ctx,
request.GetId()) when publicIPDAO is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9b52a894-193a-4433-bfd8-4d2e9abbba1d

📥 Commits

Reviewing files that changed from the base of the PR and between 0259e39 and aef3663.

📒 Files selected for processing (2)
  • internal/servers/private_public_ip_pools_server.go
  • internal/servers/private_public_ip_pools_server_test.go

@akshaynadkarni akshaynadkarni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changes LGTM

@openshift-ci openshift-ci Bot added the lgtm label Apr 29, 2026
@openshift-ci

openshift-ci Bot commented Apr 29, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: akshaynadkarni, SiddarthR56

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

@SiddarthR56
SiddarthR56 merged commit b9c88c1 into osac-project:main Apr 30, 2026
11 of 12 checks passed
akshaynadkarni added a commit to akshaynadkarni/fulfillment-service that referenced this pull request Apr 30, 2026
… Create

The pool deletion test from PR osac-project#456 creates a pool without setting its
status. Our PR adds pool validation on Create (pool must be READY with
available capacity), which caused the test to fail. Set the pool to
READY state via Update before creating a PublicIP from it.

Assisted-by: Cursor/Claude
@SiddarthR56
SiddarthR56 deleted the MGMT-23763 branch June 23, 2026 14:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants