From 943495639d1b2b52901aec54730eabe5b14d5fbd Mon Sep 17 00:00:00 2001 From: pnguyen44 Date: Mon, 18 May 2026 14:12:19 -0400 Subject: [PATCH 1/3] HYPERFLEET-1075 - feat: add force-delete endpoints for stuck clusters and nodepools --- CHANGELOG.md | 11 ++++- main.tsp | 2 +- models/common/model.tsp | 10 ++++ schemas/core/openapi.yaml | 97 +++++++++++++++++++++++++++++++++++- schemas/core/swagger.yaml | 100 +++++++++++++++++++++++++++++++++++++- schemas/gcp/openapi.yaml | 97 +++++++++++++++++++++++++++++++++++- schemas/gcp/swagger.yaml | 100 +++++++++++++++++++++++++++++++++++++- services/clusters.tsp | 19 ++++++++ services/nodepools.tsp | 21 ++++++++ 9 files changed, 451 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc5edb..22c8036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.0.15] - 2026-05-18 + +### Added + +- `ForceDeleteRequest` model with required `reason` field (HYPERFLEET-1075) +- POST `/clusters/{cluster_id}/force-delete` endpoint for force-deleting stuck clusters (HYPERFLEET-1075) +- POST `/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete` endpoint for force-deleting stuck nodepools (HYPERFLEET-1075) + ## [1.0.14] - 2026-05-15 ### Removed @@ -138,7 +146,8 @@ First official stable release of the HyperFleet API specification. - Interactive API documentation -[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.14...HEAD +[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.15...HEAD +[1.0.15]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.14...v1.0.15 [1.0.14]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.13...v1.0.14 [1.0.13]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.12...v1.0.13 [1.0.12]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.11...v1.0.12 diff --git a/main.tsp b/main.tsp index 741967c..a0f0913 100644 --- a/main.tsp +++ b/main.tsp @@ -21,7 +21,7 @@ using OpenAPI; */ @service(#{ title: "HyperFleet API" }) @info(#{ - version: "1.0.14", + version: "1.0.15", contact: #{ name: "HyperFleet Team", url: "https://github.com/openshift-hyperfleet", diff --git a/models/common/model.tsp b/models/common/model.tsp index a0489da..6ea77a1 100644 --- a/models/common/model.tsp +++ b/models/common/model.tsp @@ -263,3 +263,13 @@ model ResourceCondition { const ExampleAdapter2HealthMessage: string = "All adapter2 runtime operations completed successfully"; const ExampleAdapter2AvailableReason: string = "This adapter2 is available"; const ExampleAdapter2AvailableMessage: string = "This adapter2 is available"; + +/** + * Request body for force-delete operations + */ +model ForceDeleteRequest { + /** Reason for force-deleting the resource */ + @minLength(1) + @maxLength(1024) + reason: string; +} diff --git a/schemas/core/openapi.yaml b/schemas/core/openapi.yaml index c85003a..c7bf2bf 100644 --- a/schemas/core/openapi.yaml +++ b/schemas/core/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: HyperFleet API - version: 1.0.14 + version: 1.0.15 contact: name: HyperFleet Team url: https://github.com/openshift-hyperfleet @@ -240,6 +240,45 @@ paths: - Clusters security: - BearerAuth: [] + /api/hyperfleet/v1/clusters/{cluster_id}/force-delete: + post: + operationId: forceDeleteCluster + summary: Force-delete a cluster + description: |- + Permanently removes a cluster that is stuck in Finalizing state. + Requires a reason for audit purposes. + parameters: + - name: cluster_id + in: path + required: true + description: Cluster ID + schema: + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Clusters + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ForceDeleteRequest' + security: + - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools: get: operationId: getNodePoolsByClusterId @@ -491,6 +530,51 @@ paths: $ref: '#/components/schemas/NodePoolPatchRequest' security: - BearerAuth: [] + /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete: + post: + operationId: forceDeleteNodePool + summary: Force-delete a nodepool + description: |- + Permanently removes a nodepool that is stuck in Finalizing state. + Requires a reason for audit purposes. + parameters: + - name: cluster_id + in: path + required: true + description: Cluster ID + schema: + type: string + - name: nodepool_id + in: path + required: true + description: NodePool ID + schema: + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + tags: + - NodePools + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ForceDeleteRequest' + security: + - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses: put: operationId: putNodePoolStatuses @@ -1279,6 +1363,17 @@ components: $ref: '#/components/schemas/ValidationError' description: Field-level validation errors (for validation failures) description: RFC 9457 Problem Details error format with HyperFleet extensions + ForceDeleteRequest: + type: object + required: + - reason + properties: + reason: + type: string + minLength: 1 + maxLength: 1024 + description: Reason for force-deleting the resource + description: Request body for force-delete operations NodePool: type: object required: diff --git a/schemas/core/swagger.yaml b/schemas/core/swagger.yaml index b1ee316..5226500 100644 --- a/schemas/core/swagger.yaml +++ b/schemas/core/swagger.yaml @@ -17,7 +17,7 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' title: HyperFleet API - version: 1.0.14 + version: 1.0.15 host: hyperfleet.redhat.com basePath: / schemes: @@ -291,6 +291,47 @@ paths: description: Patch a specific cluster by ID operationId: patchClusterById summary: Patch cluster by ID + '/api/hyperfleet/v1/clusters/{cluster_id}/force-delete': + post: + consumes: + - application/json + produces: + - application/problem+json + parameters: + - description: Cluster ID + in: path + name: cluster_id + required: true + type: string + - in: body + name: body + required: true + schema: + $ref: '#/definitions/ForceDeleteRequest' + responses: + '204': + description: >- + There is no content to send for this request, but the headers may be + useful. + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Error' + security: + - BearerAuth: [] + tags: + - Clusters + description: |- + Permanently removes a cluster that is stuck in Finalizing state. + Requires a reason for audit purposes. + operationId: forceDeleteCluster + summary: Force-delete a cluster '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools': get: produces: @@ -572,6 +613,52 @@ paths: description: Patch a specific nodepool within a cluster operationId: patchNodePoolById summary: Patch nodepool by ID + '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete': + post: + consumes: + - application/json + produces: + - application/problem+json + parameters: + - description: Cluster ID + in: path + name: cluster_id + required: true + type: string + - description: NodePool ID + in: path + name: nodepool_id + required: true + type: string + - in: body + name: body + required: true + schema: + $ref: '#/definitions/ForceDeleteRequest' + responses: + '204': + description: >- + There is no content to send for this request, but the headers may be + useful. + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Error' + security: + - BearerAuth: [] + tags: + - NodePools + description: |- + Permanently removes a nodepool that is stuck in Finalizing state. + Requires a reason for audit purposes. + operationId: forceDeleteNodePool + summary: Force-delete a nodepool '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses': get: produces: @@ -1440,6 +1527,17 @@ definitions: - title - status type: object + ForceDeleteRequest: + description: Request body for force-delete operations + properties: + reason: + description: Reason for force-deleting the resource + maxLength: 1024 + minLength: 1 + type: string + required: + - reason + type: object NodePool: example: created_by: user-123@example.com diff --git a/schemas/gcp/openapi.yaml b/schemas/gcp/openapi.yaml index 3b624a0..97d5fcb 100644 --- a/schemas/gcp/openapi.yaml +++ b/schemas/gcp/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: HyperFleet API - version: 1.0.14 + version: 1.0.15 contact: name: HyperFleet Team url: https://github.com/openshift-hyperfleet @@ -259,6 +259,45 @@ paths: - Clusters security: - BearerAuth: [] + /api/hyperfleet/v1/clusters/{cluster_id}/force-delete: + post: + operationId: forceDeleteCluster + summary: Force-delete a cluster + description: |- + Permanently removes a cluster that is stuck in Finalizing state. + Requires a reason for audit purposes. + parameters: + - name: cluster_id + in: path + required: true + description: Cluster ID + schema: + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Clusters + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ForceDeleteRequest' + security: + - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools: get: operationId: getNodePoolsByClusterId @@ -529,6 +568,51 @@ paths: $ref: '#/components/schemas/NodePoolPatchRequest' security: - BearerAuth: [] + /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete: + post: + operationId: forceDeleteNodePool + summary: Force-delete a nodepool + description: |- + Permanently removes a nodepool that is stuck in Finalizing state. + Requires a reason for audit purposes. + parameters: + - name: cluster_id + in: path + required: true + description: Cluster ID + schema: + type: string + - name: nodepool_id + in: path + required: true + description: NodePool ID + schema: + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + tags: + - NodePools + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ForceDeleteRequest' + security: + - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses: get: operationId: getNodePoolsStatuses @@ -1303,6 +1387,17 @@ components: $ref: '#/components/schemas/ValidationError' description: Field-level validation errors (for validation failures) description: RFC 9457 Problem Details error format with HyperFleet extensions + ForceDeleteRequest: + type: object + required: + - reason + properties: + reason: + type: string + minLength: 1 + maxLength: 1024 + description: Reason for force-deleting the resource + description: Request body for force-delete operations NetworkingSpec: type: object properties: diff --git a/schemas/gcp/swagger.yaml b/schemas/gcp/swagger.yaml index c3801b6..a5c9be7 100644 --- a/schemas/gcp/swagger.yaml +++ b/schemas/gcp/swagger.yaml @@ -17,7 +17,7 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' title: HyperFleet API - version: 1.0.14 + version: 1.0.15 host: hyperfleet.redhat.com basePath: / schemes: @@ -311,6 +311,47 @@ paths: description: Patch a specific cluster by ID operationId: patchClusterById summary: Patch cluster by ID + '/api/hyperfleet/v1/clusters/{cluster_id}/force-delete': + post: + consumes: + - application/json + produces: + - application/problem+json + parameters: + - description: Cluster ID + in: path + name: cluster_id + required: true + type: string + - in: body + name: body + required: true + schema: + $ref: '#/definitions/ForceDeleteRequest' + responses: + '204': + description: >- + There is no content to send for this request, but the headers may be + useful. + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Error' + security: + - BearerAuth: [] + tags: + - Clusters + description: |- + Permanently removes a cluster that is stuck in Finalizing state. + Requires a reason for audit purposes. + operationId: forceDeleteCluster + summary: Force-delete a cluster '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools': get: produces: @@ -611,6 +652,52 @@ paths: description: Patch a specific nodepool within a cluster operationId: patchNodePoolById summary: Patch nodepool by ID + '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete': + post: + consumes: + - application/json + produces: + - application/problem+json + parameters: + - description: Cluster ID + in: path + name: cluster_id + required: true + type: string + - description: NodePool ID + in: path + name: nodepool_id + required: true + type: string + - in: body + name: body + required: true + schema: + $ref: '#/definitions/ForceDeleteRequest' + responses: + '204': + description: >- + There is no content to send for this request, but the headers may be + useful. + '400': + description: The server could not understand the request due to invalid syntax. + '404': + description: The server cannot find the requested resource. + '409': + description: The request conflicts with the current state of the server. + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Error' + security: + - BearerAuth: [] + tags: + - NodePools + description: |- + Permanently removes a nodepool that is stuck in Finalizing state. + Requires a reason for audit purposes. + operationId: forceDeleteNodePool + summary: Force-delete a nodepool '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses': get: produces: @@ -1450,6 +1537,17 @@ definitions: - title - status type: object + ForceDeleteRequest: + description: Request body for force-delete operations + properties: + reason: + description: Reason for force-deleting the resource + maxLength: 1024 + minLength: 1 + type: string + required: + - reason + type: object NetworkingSpec: properties: clusterNetwork: diff --git a/services/clusters.tsp b/services/clusters.tsp index be45d27..0a8a1cf 100644 --- a/services/clusters.tsp +++ b/services/clusters.tsp @@ -90,4 +90,23 @@ interface Clusters { | Error | BadRequestResponse; + /** + * Permanently removes a cluster that is stuck in Finalizing state. + * Requires a reason for audit purposes. + */ + @route("/{cluster_id}/force-delete") + @post + @summary("Force-delete a cluster") + @operationId("forceDeleteCluster") + forceDeleteCluster( + /** Cluster ID */ + @path cluster_id: string, + @body body: ForceDeleteRequest, + ): { + @statusCode statusCode: 204; + } | NotFoundResponse + | Error + | BadRequestResponse + | ConflictResponse; + } diff --git a/services/nodepools.tsp b/services/nodepools.tsp index ffe5640..916209e 100644 --- a/services/nodepools.tsp +++ b/services/nodepools.tsp @@ -95,6 +95,27 @@ interface NodePools { | Error | BadRequestResponse; + /** + * Permanently removes a nodepool that is stuck in Finalizing state. + * Requires a reason for audit purposes. + */ + @route("/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete") + @post + @summary("Force-delete a nodepool") + @operationId("forceDeleteNodePool") + forceDeleteNodePool( + /** Cluster ID */ + @path cluster_id: string, + /** NodePool ID */ + @path nodepool_id: string, + @body body: ForceDeleteRequest, + ): { + @statusCode statusCode: 204; + } | NotFoundResponse + | Error + | BadRequestResponse + | ConflictResponse; + /** * Patch a specific nodepool within a cluster */ From 8bd6bcd783846fc4edbd4fbefb63d8069bcadb3f Mon Sep 17 00:00:00 2001 From: pnguyen44 Date: Mon, 18 May 2026 15:06:08 -0400 Subject: [PATCH 2/3] HYPERFLEET-1075 - docs: Clarify force-delete is database-only --- schemas/core/openapi.yaml | 8 ++++---- schemas/core/swagger.yaml | 16 ++++++++++------ schemas/gcp/openapi.yaml | 8 ++++---- schemas/gcp/swagger.yaml | 16 ++++++++++------ services/clusters.tsp | 4 ++-- services/nodepools.tsp | 4 ++-- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/schemas/core/openapi.yaml b/schemas/core/openapi.yaml index c7bf2bf..8f7dba8 100644 --- a/schemas/core/openapi.yaml +++ b/schemas/core/openapi.yaml @@ -245,8 +245,8 @@ paths: operationId: forceDeleteCluster summary: Force-delete a cluster description: |- - Permanently removes a cluster that is stuck in Finalizing state. - Requires a reason for audit purposes. + Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. + This is a database-only operation. Requires a reason for audit purposes. parameters: - name: cluster_id in: path @@ -535,8 +535,8 @@ paths: operationId: forceDeleteNodePool summary: Force-delete a nodepool description: |- - Permanently removes a nodepool that is stuck in Finalizing state. - Requires a reason for audit purposes. + Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. + This is a database-only operation. Requires a reason for audit purposes. parameters: - name: cluster_id in: path diff --git a/schemas/core/swagger.yaml b/schemas/core/swagger.yaml index 5226500..a8fa335 100644 --- a/schemas/core/swagger.yaml +++ b/schemas/core/swagger.yaml @@ -327,9 +327,11 @@ paths: - BearerAuth: [] tags: - Clusters - description: |- - Permanently removes a cluster that is stuck in Finalizing state. - Requires a reason for audit purposes. + description: >- + Permanently removes the cluster record from the database for a cluster + stuck in Finalizing state. + + This is a database-only operation. Requires a reason for audit purposes. operationId: forceDeleteCluster summary: Force-delete a cluster '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools': @@ -654,9 +656,11 @@ paths: - BearerAuth: [] tags: - NodePools - description: |- - Permanently removes a nodepool that is stuck in Finalizing state. - Requires a reason for audit purposes. + description: >- + Permanently removes the nodepool record from the database for a nodepool + stuck in Finalizing state. + + This is a database-only operation. Requires a reason for audit purposes. operationId: forceDeleteNodePool summary: Force-delete a nodepool '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses': diff --git a/schemas/gcp/openapi.yaml b/schemas/gcp/openapi.yaml index 97d5fcb..1176bbc 100644 --- a/schemas/gcp/openapi.yaml +++ b/schemas/gcp/openapi.yaml @@ -264,8 +264,8 @@ paths: operationId: forceDeleteCluster summary: Force-delete a cluster description: |- - Permanently removes a cluster that is stuck in Finalizing state. - Requires a reason for audit purposes. + Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. + This is a database-only operation. Requires a reason for audit purposes. parameters: - name: cluster_id in: path @@ -573,8 +573,8 @@ paths: operationId: forceDeleteNodePool summary: Force-delete a nodepool description: |- - Permanently removes a nodepool that is stuck in Finalizing state. - Requires a reason for audit purposes. + Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. + This is a database-only operation. Requires a reason for audit purposes. parameters: - name: cluster_id in: path diff --git a/schemas/gcp/swagger.yaml b/schemas/gcp/swagger.yaml index a5c9be7..cdaeece 100644 --- a/schemas/gcp/swagger.yaml +++ b/schemas/gcp/swagger.yaml @@ -347,9 +347,11 @@ paths: - BearerAuth: [] tags: - Clusters - description: |- - Permanently removes a cluster that is stuck in Finalizing state. - Requires a reason for audit purposes. + description: >- + Permanently removes the cluster record from the database for a cluster + stuck in Finalizing state. + + This is a database-only operation. Requires a reason for audit purposes. operationId: forceDeleteCluster summary: Force-delete a cluster '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools': @@ -693,9 +695,11 @@ paths: - BearerAuth: [] tags: - NodePools - description: |- - Permanently removes a nodepool that is stuck in Finalizing state. - Requires a reason for audit purposes. + description: >- + Permanently removes the nodepool record from the database for a nodepool + stuck in Finalizing state. + + This is a database-only operation. Requires a reason for audit purposes. operationId: forceDeleteNodePool summary: Force-delete a nodepool '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses': diff --git a/services/clusters.tsp b/services/clusters.tsp index 0a8a1cf..5799a29 100644 --- a/services/clusters.tsp +++ b/services/clusters.tsp @@ -91,8 +91,8 @@ interface Clusters { | BadRequestResponse; /** - * Permanently removes a cluster that is stuck in Finalizing state. - * Requires a reason for audit purposes. + * Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. + * This is a database-only operation. Requires a reason for audit purposes. */ @route("/{cluster_id}/force-delete") @post diff --git a/services/nodepools.tsp b/services/nodepools.tsp index 916209e..c0b71ef 100644 --- a/services/nodepools.tsp +++ b/services/nodepools.tsp @@ -96,8 +96,8 @@ interface NodePools { | BadRequestResponse; /** - * Permanently removes a nodepool that is stuck in Finalizing state. - * Requires a reason for audit purposes. + * Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. + * This is a database-only operation. Requires a reason for audit purposes. */ @route("/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete") @post From 47399d4d42d6b8e62c82f4864ce7fa8d014f2ec1 Mon Sep 17 00:00:00 2001 From: pnguyen44 Date: Tue, 19 May 2026 11:25:54 -0400 Subject: [PATCH 3/3] HYPERFLEET-1075 - feat: Move force-delete endpoints to internal-only (core spec) --- CHANGELOG.md | 4 +- aliases-core.tsp | 3 +- schemas/gcp/openapi.yaml | 95 --------------------------- schemas/gcp/swagger.yaml | 102 ----------------------------- services/clusters.tsp | 19 ------ services/force-delete-internal.tsp | 60 +++++++++++++++++ services/nodepools.tsp | 21 ------ 7 files changed, 64 insertions(+), 240 deletions(-) create mode 100644 services/force-delete-internal.tsp diff --git a/CHANGELOG.md b/CHANGELOG.md index 22c8036..d3d205e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `ForceDeleteRequest` model with required `reason` field (HYPERFLEET-1075) -- POST `/clusters/{cluster_id}/force-delete` endpoint for force-deleting stuck clusters (HYPERFLEET-1075) -- POST `/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete` endpoint for force-deleting stuck nodepools (HYPERFLEET-1075) +- POST `/clusters/{cluster_id}/force-delete` internal endpoint for force-deleting stuck clusters (HYPERFLEET-1075) +- POST `/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete` internal endpoint for force-deleting stuck nodepools (HYPERFLEET-1075) ## [1.0.14] - 2026-05-15 diff --git a/aliases-core.tsp b/aliases-core.tsp index e0943ba..b972f47 100644 --- a/aliases-core.tsp +++ b/aliases-core.tsp @@ -6,4 +6,5 @@ import "./models-core/nodepool/model.tsp"; import "./models-core/nodepool/example_nodepool.tsp"; import "./models-core/nodepool/example_post.tsp"; import "./models-core/nodepool/example_patch.tsp"; -import "./services/statuses-internal.tsp"; \ No newline at end of file +import "./services/statuses-internal.tsp"; +import "./services/force-delete-internal.tsp"; diff --git a/schemas/gcp/openapi.yaml b/schemas/gcp/openapi.yaml index 1176bbc..beb77b7 100644 --- a/schemas/gcp/openapi.yaml +++ b/schemas/gcp/openapi.yaml @@ -259,45 +259,6 @@ paths: - Clusters security: - BearerAuth: [] - /api/hyperfleet/v1/clusters/{cluster_id}/force-delete: - post: - operationId: forceDeleteCluster - summary: Force-delete a cluster - description: |- - Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. - This is a database-only operation. Requires a reason for audit purposes. - parameters: - - name: cluster_id - in: path - required: true - description: Cluster ID - schema: - type: string - responses: - '204': - description: 'There is no content to send for this request, but the headers may be useful. ' - '400': - description: The server could not understand the request due to invalid syntax. - '404': - description: The server cannot find the requested resource. - '409': - description: The request conflicts with the current state of the server. - default: - description: An unexpected error response. - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Error' - tags: - - Clusters - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ForceDeleteRequest' - security: - - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools: get: operationId: getNodePoolsByClusterId @@ -568,51 +529,6 @@ paths: $ref: '#/components/schemas/NodePoolPatchRequest' security: - BearerAuth: [] - /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete: - post: - operationId: forceDeleteNodePool - summary: Force-delete a nodepool - description: |- - Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. - This is a database-only operation. Requires a reason for audit purposes. - parameters: - - name: cluster_id - in: path - required: true - description: Cluster ID - schema: - type: string - - name: nodepool_id - in: path - required: true - description: NodePool ID - schema: - type: string - responses: - '204': - description: 'There is no content to send for this request, but the headers may be useful. ' - '400': - description: The server could not understand the request due to invalid syntax. - '404': - description: The server cannot find the requested resource. - '409': - description: The request conflicts with the current state of the server. - default: - description: An unexpected error response. - content: - application/problem+json: - schema: - $ref: '#/components/schemas/Error' - tags: - - NodePools - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ForceDeleteRequest' - security: - - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses: get: operationId: getNodePoolsStatuses @@ -1387,17 +1303,6 @@ components: $ref: '#/components/schemas/ValidationError' description: Field-level validation errors (for validation failures) description: RFC 9457 Problem Details error format with HyperFleet extensions - ForceDeleteRequest: - type: object - required: - - reason - properties: - reason: - type: string - minLength: 1 - maxLength: 1024 - description: Reason for force-deleting the resource - description: Request body for force-delete operations NetworkingSpec: type: object properties: diff --git a/schemas/gcp/swagger.yaml b/schemas/gcp/swagger.yaml index cdaeece..2de8b5b 100644 --- a/schemas/gcp/swagger.yaml +++ b/schemas/gcp/swagger.yaml @@ -311,49 +311,6 @@ paths: description: Patch a specific cluster by ID operationId: patchClusterById summary: Patch cluster by ID - '/api/hyperfleet/v1/clusters/{cluster_id}/force-delete': - post: - consumes: - - application/json - produces: - - application/problem+json - parameters: - - description: Cluster ID - in: path - name: cluster_id - required: true - type: string - - in: body - name: body - required: true - schema: - $ref: '#/definitions/ForceDeleteRequest' - responses: - '204': - description: >- - There is no content to send for this request, but the headers may be - useful. - '400': - description: The server could not understand the request due to invalid syntax. - '404': - description: The server cannot find the requested resource. - '409': - description: The request conflicts with the current state of the server. - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/Error' - security: - - BearerAuth: [] - tags: - - Clusters - description: >- - Permanently removes the cluster record from the database for a cluster - stuck in Finalizing state. - - This is a database-only operation. Requires a reason for audit purposes. - operationId: forceDeleteCluster - summary: Force-delete a cluster '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools': get: produces: @@ -654,54 +611,6 @@ paths: description: Patch a specific nodepool within a cluster operationId: patchNodePoolById summary: Patch nodepool by ID - '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete': - post: - consumes: - - application/json - produces: - - application/problem+json - parameters: - - description: Cluster ID - in: path - name: cluster_id - required: true - type: string - - description: NodePool ID - in: path - name: nodepool_id - required: true - type: string - - in: body - name: body - required: true - schema: - $ref: '#/definitions/ForceDeleteRequest' - responses: - '204': - description: >- - There is no content to send for this request, but the headers may be - useful. - '400': - description: The server could not understand the request due to invalid syntax. - '404': - description: The server cannot find the requested resource. - '409': - description: The request conflicts with the current state of the server. - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/Error' - security: - - BearerAuth: [] - tags: - - NodePools - description: >- - Permanently removes the nodepool record from the database for a nodepool - stuck in Finalizing state. - - This is a database-only operation. Requires a reason for audit purposes. - operationId: forceDeleteNodePool - summary: Force-delete a nodepool '/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses': get: produces: @@ -1541,17 +1450,6 @@ definitions: - title - status type: object - ForceDeleteRequest: - description: Request body for force-delete operations - properties: - reason: - description: Reason for force-deleting the resource - maxLength: 1024 - minLength: 1 - type: string - required: - - reason - type: object NetworkingSpec: properties: clusterNetwork: diff --git a/services/clusters.tsp b/services/clusters.tsp index 5799a29..be45d27 100644 --- a/services/clusters.tsp +++ b/services/clusters.tsp @@ -90,23 +90,4 @@ interface Clusters { | Error | BadRequestResponse; - /** - * Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. - * This is a database-only operation. Requires a reason for audit purposes. - */ - @route("/{cluster_id}/force-delete") - @post - @summary("Force-delete a cluster") - @operationId("forceDeleteCluster") - forceDeleteCluster( - /** Cluster ID */ - @path cluster_id: string, - @body body: ForceDeleteRequest, - ): { - @statusCode statusCode: 204; - } | NotFoundResponse - | Error - | BadRequestResponse - | ConflictResponse; - } diff --git a/services/force-delete-internal.tsp b/services/force-delete-internal.tsp new file mode 100644 index 0000000..ae72d58 --- /dev/null +++ b/services/force-delete-internal.tsp @@ -0,0 +1,60 @@ +import "@typespec/http"; +import "@typespec/openapi"; +import "@typespec/openapi3"; + +import "../models/common/model.tsp"; + +using Http; +using OpenAPI; + +namespace HyperFleet; + +@tag("Clusters") +@route("/clusters") +@useAuth(HyperFleet.BearerAuth) +interface ClustersForceDelete { + /** + * Permanently removes the cluster record from the database for a cluster stuck in Finalizing state. + * This is a database-only operation. Requires a reason for audit purposes. + */ + @route("/{cluster_id}/force-delete") + @post + @summary("Force-delete a cluster") + @operationId("forceDeleteCluster") + forceDeleteCluster( + /** Cluster ID */ + @path cluster_id: string, + @body body: ForceDeleteRequest, + ): { + @statusCode statusCode: 204; + } | Error + | NotFoundResponse + | BadRequestResponse + | ConflictResponse; +} + +@tag("NodePools") +@route("/clusters/{cluster_id}/nodepools") +@useAuth(HyperFleet.BearerAuth) +interface NodePoolsForceDelete { + /** + * Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. + * This is a database-only operation. Requires a reason for audit purposes. + */ + @route("/{nodepool_id}/force-delete") + @post + @summary("Force-delete a nodepool") + @operationId("forceDeleteNodePool") + forceDeleteNodePool( + /** Cluster ID */ + @path cluster_id: string, + /** NodePool ID */ + @path nodepool_id: string, + @body body: ForceDeleteRequest, + ): { + @statusCode statusCode: 204; + } | Error + | NotFoundResponse + | BadRequestResponse + | ConflictResponse; +} diff --git a/services/nodepools.tsp b/services/nodepools.tsp index c0b71ef..ffe5640 100644 --- a/services/nodepools.tsp +++ b/services/nodepools.tsp @@ -95,27 +95,6 @@ interface NodePools { | Error | BadRequestResponse; - /** - * Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state. - * This is a database-only operation. Requires a reason for audit purposes. - */ - @route("/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete") - @post - @summary("Force-delete a nodepool") - @operationId("forceDeleteNodePool") - forceDeleteNodePool( - /** Cluster ID */ - @path cluster_id: string, - /** NodePool ID */ - @path nodepool_id: string, - @body body: ForceDeleteRequest, - ): { - @statusCode statusCode: 204; - } | NotFoundResponse - | Error - | BadRequestResponse - | ConflictResponse; - /** * Patch a specific nodepool within a cluster */