Skip to content

HYPERFLEET-971 - feat: reject nodepool create/patch on soft-deleted cluster #113

Merged
openshift-merge-bot[bot] merged 2 commits intoopenshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-971
May 5, 2026
Merged

HYPERFLEET-971 - feat: reject nodepool create/patch on soft-deleted cluster #113
openshift-merge-bot[bot] merged 2 commits intoopenshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-971

Conversation

@kuudori
Copy link
Copy Markdown
Contributor

@kuudori kuudori commented Apr 23, 2026

Prevent nodepool creation and updates on clusters marked for deletion by returning 409 Conflict, avoiding orphaned resources after cluster cleanup.

  • Add ConflictState error constructor with HYPERFLEET-CNF-003 code
  • Check cluster.DeletedTime in Create and Patch handlers before proceeding
  • Check nodepool.DeletedTime in Patch handler before proceeding
  • Reject cluster patch on soft-deleted cluster with 409 Conflict
  • Add unit tests for 409 rejection and happy-path scenarios on all endpoints

Summary

  • HYPERFLEET-971

Test Plan

  • Unit tests added/updated
  • make test-all passes
  • make lint passes
  • Helm chart changes validated with make test-helm (if applicable)
  • Deployed to a development cluster and verified (if Helm/config changes)
  • E2E tests passed (if cross-component or major changes)

Summary by CodeRabbit

  • Bug Fixes

    • API now prevents create/patch when a cluster or node pool is marked for deletion, returning 409 Conflict with a "marked for deletion" detail.
  • New Features

    • Added a dedicated RFC 9457 conflict error code (HYPERFLEET-CNF-003) and consistent conflict responses for state-conflict cases.
  • Tests

    • Expanded tests for create/patch flows, soft-delete conflict scenarios, and expected error payloads.
  • Documentation

    • OpenAPI spec updated to document 409 conflict responses and bumped version.

@openshift-ci openshift-ci Bot requested review from jsell-rh and vkareh April 23, 2026 16:59
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Walkthrough

Adds a new RFC 9457 conflict error code HYPERFLEET-CNF-003 (CodeConflictState) and constructor ConflictState(...) in pkg/errors. Handlers now check soft-delete preconditions and return this conflict when a cluster or nodepool has DeletedTime != nil. Cluster PATCH and nodepool CREATE/PATCH were updated to abort with 409 in those cases. OpenAPI was updated to document 409 responses. Tests were added/updated to cover success, conflict (409 with the new code and "marked for deletion" detail), and not-found (404) scenarios.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Handler as ClusterHandler / NodePoolsHandler
  participant Service as ClusterService / NodePoolService
  participant Errors as errors.Registry

  Client->>Handler: HTTP PATCH/POST (cluster/nodepool)
  Handler->>Service: Get(clusterID)
  Service-->>Handler: cluster (maybe nil)
  alt cluster missing
    Handler-->>Client: 404 Not Found
  else cluster present
    alt cluster.DeletedTime != nil
      Handler->>Errors: ConflictState("Cluster '%s' is marked for deletion", id)
      Errors-->>Handler: ProblemDetails (409, HYPERFLEET-CNF-003)
      Handler-->>Client: 409 Conflict (problem-details)
    else cluster active
      opt nodepool flow
        Handler->>Service: Get(nodepoolID)
        Service-->>Handler: nodepool (maybe nil)
        alt nodepool missing
          Handler-->>Client: 404 Not Found
        else nodepool present
          alt nodepool.DeletedTime != nil
            Handler->>Errors: ConflictState("Nodepool '%s' is marked for deletion", id)
            Errors-->>Handler: ProblemDetails (409, HYPERFLEET-CNF-003)
            Handler-->>Client: 409 Conflict
          else nodepool active
            Handler->>Service: Replace/Create(...)
            Service-->>Handler: updated resource
            Handler-->>Client: 200/201 with body
          end
        end
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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 directly describes the main feature: preventing nodepool create/patch operations on soft-deleted clusters with 409 Conflict response, which is the core objective of this changeset.
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

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

@openshift-hyperfleet openshift-hyperfleet deleted a comment from coderabbitai Bot Apr 23, 2026
@openshift-hyperfleet openshift-hyperfleet deleted a comment from coderabbitai Bot Apr 23, 2026
@kuudori
Copy link
Copy Markdown
Contributor Author

kuudori commented Apr 23, 2026

accidentally clicked generate unit tests under the coderabbit comment, deleted his comments with an unsuccessful attempt to do that

@rafabene
Copy link
Copy Markdown
Contributor

Nice work! The soft-delete guards on the cluster-scoped endpoints look solid.

One thing I noticed: the standalone NodePoolHandler.Patch in pkg/handlers/node_pool.go doesn't have the same DeletedTime check you added to ClusterNodePoolsHandler.Patch. So a deleted nodepool could still be patched through PATCH /nodepools/:id even though the nested endpoint now correctly rejects it with 409.

Worth adding the same guard there for consistency — or at least tracking it as a follow-up.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/handlers/cluster_nodepools.go`:
- Around line 193-200: The soft-delete check on cluster.DeletedTime in the
handler (after clusterService.Get) is racy; move this validation into the
service/DAO layer and enforce it atomically with the write. Update the nodepool
creation/replacement paths (the service methods that call Create()/Replace() on
the DAO) to either re-check DeletedTime inside the same transaction or add a
precondition to the DB write (e.g., include WHERE cluster.deleted_time IS NULL
in the INSERT/UPDATE SQL) so Create()/Replace() fail if the cluster is marked
deleted; ensure the service/DAO returns a clear ConflictState error when that
condition is hit.
🪄 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: Enterprise

Run ID: 9ca03664-3434-4b51-9885-8613945b3352

📥 Commits

Reviewing files that changed from the base of the PR and between 0dcbabd and 1c2efd3.

📒 Files selected for processing (5)
  • pkg/errors/errors.go
  • pkg/handlers/cluster.go
  • pkg/handlers/cluster_nodepools.go
  • pkg/handlers/cluster_nodepools_test.go
  • pkg/handlers/cluster_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/errors/errors.go
  • pkg/handlers/cluster.go

Comment thread pkg/handlers/cluster_nodepools.go
@rafabene
Copy link
Copy Markdown
Contributor

/retest

Comment thread pkg/handlers/cluster_nodepools.go
Comment thread pkg/errors/errors.go Outdated
Comment thread pkg/handlers/cluster_nodepools_test.go
@rafabene
Copy link
Copy Markdown
Contributor

Nice work on the soft-delete guards! One thing to consider as a follow-up:

The status endpoints (ClusterStatusHandler.Create and NodePoolStatusHandler.Create) don't have the same DeletedTime check. Adapters can still report status on soft-deleted clusters/nodepools. This may be intentional (adapters need to report back during deletion), but worth an explicit decision — and possibly a follow-up ticket if we want to guard those too.

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

🤖 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 `@openapi/openapi.yaml`:
- Around line 128-129: Add a 409 response body schema for the three explicit
conflict responses so the OpenAPI clients know the shape: for each endpoint
PATCH /api/hyperfleet/v1/clusters/{cluster_id}, POST
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools, and PATCH
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id} replace the
bare 409 description with a content section declaring "application/problem+json"
and reference the Problem Details schema (or inline the RFC7807-like fields:
type, title, status, detail, instance) so generated clients can validate the
conflict response. Ensure the media type key is "application/problem+json" and
the schema is either $ref to the shared ProblemDetails component or an
equivalent object schema.
🪄 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: Enterprise

Run ID: e6a7ef09-e98d-4fdb-badd-6066a0ac1b56

📥 Commits

Reviewing files that changed from the base of the PR and between 894aac3 and 65f3aad.

📒 Files selected for processing (5)
  • openapi/openapi.yaml
  • pkg/errors/errors.go
  • pkg/handlers/cluster.go
  • pkg/handlers/cluster_nodepools_test.go
  • pkg/handlers/cluster_test.go
✅ Files skipped from review due to trivial changes (2)
  • pkg/errors/errors.go
  • pkg/handlers/cluster_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/handlers/cluster.go
  • pkg/handlers/cluster_nodepools_test.go

Comment thread openapi/openapi.yaml
nodePoolID := testNodePoolID

tests := []struct {
setupMocks func(ctrl *gomock.Controller) ( //nolint:lll
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.

Just minor thing not related to your changes. I notice we have these //nolint:lll on multiple lines in this file. Since code it well formatted, this doesn't have to be there. If you feel up to it, you can remove them

@rafabene
Copy link
Copy Markdown
Contributor

rafabene commented May 5, 2026

/lgtm

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rafabene

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 openshift-ci Bot added the approved label May 5, 2026
@openshift-merge-bot openshift-merge-bot Bot merged commit 20f9bf8 into openshift-hyperfleet:main May 5, 2026
9 checks passed
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.

3 participants