-
Notifications
You must be signed in to change notification settings - Fork 12
HYPERFLEET-619 - fix: Prevent duplicate nodepool names within a cluster #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,10 @@ import ( | |
| "github.com/openshift-hyperfleet/hyperfleet-api/test" | ||
| ) | ||
|
|
||
| const ( | ||
| kindNodePool = "NodePool" | ||
| ) | ||
|
|
||
| // TestNodePoolGet is disabled because GET /nodepools/{id} is not in the OpenAPI spec | ||
| // The API only supports: | ||
| // - GET /api/hyperfleet/v1/nodepools (list all nodepools) | ||
|
|
@@ -58,7 +62,7 @@ func TestNodePoolPost(t *testing.T) { | |
| Expect(err).NotTo(HaveOccurred()) | ||
|
|
||
| // POST responses per openapi spec: 201, 409, 500 | ||
| kind := "NodePool" | ||
| kind := kindNodePool | ||
| nodePoolInput := openapi.NodePoolCreateRequest{ | ||
| Kind: &kind, | ||
| Name: "test-name", | ||
|
|
@@ -190,7 +194,7 @@ func TestGetNodePoolByClusterIdAndNodePoolId(t *testing.T) { | |
| Expect(err).NotTo(HaveOccurred()) | ||
|
|
||
| // Create a nodepool for this cluster using the API | ||
| kind := "NodePool" | ||
| kind := kindNodePool | ||
| nodePoolInput := openapi.NodePoolCreateRequest{ | ||
| Kind: &kind, | ||
| Name: "test-np-get", | ||
|
|
@@ -319,3 +323,48 @@ func TestNodePoolPost_WrongKind(t *testing.T) { | |
| Expect(ok).To(BeTrue()) | ||
| Expect(detail).To(ContainSubstring("kind must be 'NodePool'")) | ||
| } | ||
|
|
||
| // TestNodePoolDuplicateNames tests that duplicate nodepool names within a cluster are rejected | ||
| func TestNodePoolDuplicateNames(t *testing.T) { | ||
| h, client := test.RegisterIntegration(t) | ||
|
|
||
| account := h.NewRandAccount() | ||
| ctx := h.NewAuthenticatedContext(account) | ||
|
|
||
| // Create a cluster first | ||
| cluster, err := h.Factories.NewClusters(h.NewID()) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
|
|
||
| // Create first nodepool with a specific name | ||
| kind := kindNodePool | ||
| nodePoolInput := openapi.NodePoolCreateRequest{ | ||
| Kind: &kind, | ||
| Name: "test-duplicate", | ||
| Spec: map[string]interface{}{"test": "spec"}, | ||
| } | ||
|
|
||
| resp, err := client.CreateNodePoolWithResponse( | ||
| ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(nodePoolInput), test.WithAuthToken(ctx), | ||
| ) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) | ||
|
|
||
| // Create second nodepool with the same name in the same cluster | ||
| resp, err = client.CreateNodePoolWithResponse( | ||
| ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(nodePoolInput), test.WithAuthToken(ctx), | ||
| ) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(resp.StatusCode()). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider also asserting the response body for the 409 case, not just the status code. Per the For example: // After asserting StatusCode == 409:
Expect(resp.JSON409).NotTo(BeNil())
// or parse the body and check:
// Expect(problemDetail.Code).To(Equal("HYPERFLEET-CNF-001"))
// Expect(problemDetail.Detail).To(ContainSubstring("already exists"))
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assertions to validate the error response body added. |
||
| To(Equal(http.StatusConflict), "Expected 409 Conflict for duplicate nodepool name in same cluster") | ||
|
|
||
| Expect(resp.ApplicationproblemJSONDefault).NotTo(BeNil(), "Expected response body to be present") | ||
| problemDetail := resp.ApplicationproblemJSONDefault | ||
|
|
||
| Expect(*problemDetail.Code).To(Equal("HYPERFLEET-CNF-001"), "Expected conflict error code") | ||
| Expect(problemDetail.Type).To(Equal("https://api.hyperfleet.io/errors/conflict"), "Expected conflict error type") | ||
| Expect(problemDetail.Title).To(Equal("Resource Conflict"), "Expected conflict error title") | ||
|
|
||
| Expect(problemDetail.Detail).NotTo(BeNil(), "Expected error detail to be present") | ||
| Expect(*problemDetail.Detail).To(ContainSubstring("already exists"), | ||
| "Expected error detail to mention that resource already exists") | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle existing duplicate nodepool names before creating the unique index.
Line 66–69 will fail the migration if any existing
(owner_id, name)duplicates exist among non-deleted rows, which can block upgrades on live clusters. Consider a preflight duplicate check with a clear error (or a cleanup step) before creating the index.🧪 Suggested preflight check (with explicit error)
📝 Committable suggestion
🤖 Prompt for AI Agents