Skip to content

Commit aeb51fc

Browse files
Merge pull request #48 from pnguyen44/HYPERFLEET-1075/force-delete
HYPERFLEET-1075 - feat: add force-delete endpoints for stuck clusters and nodepools
2 parents 2b7675f + 47399d4 commit aeb51fc

9 files changed

Lines changed: 284 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.0.15] - 2026-05-18
11+
12+
### Added
13+
14+
- `ForceDeleteRequest` model with required `reason` field (HYPERFLEET-1075)
15+
- POST `/clusters/{cluster_id}/force-delete` internal endpoint for force-deleting stuck clusters (HYPERFLEET-1075)
16+
- POST `/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete` internal endpoint for force-deleting stuck nodepools (HYPERFLEET-1075)
17+
1018
## [1.0.14] - 2026-05-15
1119

1220
### Removed
@@ -138,7 +146,8 @@ First official stable release of the HyperFleet API specification.
138146
- Interactive API documentation
139147

140148
<!-- Links -->
141-
[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.14...HEAD
149+
[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.15...HEAD
150+
[1.0.15]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.14...v1.0.15
142151
[1.0.14]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.13...v1.0.14
143152
[1.0.13]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.12...v1.0.13
144153
[1.0.12]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.11...v1.0.12

aliases-core.tsp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ import "./models-core/nodepool/model.tsp";
66
import "./models-core/nodepool/example_nodepool.tsp";
77
import "./models-core/nodepool/example_post.tsp";
88
import "./models-core/nodepool/example_patch.tsp";
9-
import "./services/statuses-internal.tsp";
9+
import "./services/statuses-internal.tsp";
10+
import "./services/force-delete-internal.tsp";

main.tsp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ using OpenAPI;
2121
*/
2222
@service(#{ title: "HyperFleet API" })
2323
@info(#{
24-
version: "1.0.14",
24+
version: "1.0.15",
2525
contact: #{
2626
name: "HyperFleet Team",
2727
url: "https://github.com/openshift-hyperfleet",

models/common/model.tsp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,13 @@ model ResourceCondition {
263263
const ExampleAdapter2HealthMessage: string = "All adapter2 runtime operations completed successfully";
264264
const ExampleAdapter2AvailableReason: string = "This adapter2 is available";
265265
const ExampleAdapter2AvailableMessage: string = "This adapter2 is available";
266+
267+
/**
268+
* Request body for force-delete operations
269+
*/
270+
model ForceDeleteRequest {
271+
/** Reason for force-deleting the resource */
272+
@minLength(1)
273+
@maxLength(1024)
274+
reason: string;
275+
}

schemas/core/openapi.yaml

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
openapi: 3.0.0
22
info:
33
title: HyperFleet API
4-
version: 1.0.14
4+
version: 1.0.15
55
contact:
66
name: HyperFleet Team
77
url: https://github.com/openshift-hyperfleet
@@ -240,6 +240,45 @@ paths:
240240
- Clusters
241241
security:
242242
- BearerAuth: []
243+
/api/hyperfleet/v1/clusters/{cluster_id}/force-delete:
244+
post:
245+
operationId: forceDeleteCluster
246+
summary: Force-delete a cluster
247+
description: |-
248+
Permanently removes the cluster record from the database for a cluster stuck in Finalizing state.
249+
This is a database-only operation. Requires a reason for audit purposes.
250+
parameters:
251+
- name: cluster_id
252+
in: path
253+
required: true
254+
description: Cluster ID
255+
schema:
256+
type: string
257+
responses:
258+
'204':
259+
description: 'There is no content to send for this request, but the headers may be useful. '
260+
'400':
261+
description: The server could not understand the request due to invalid syntax.
262+
'404':
263+
description: The server cannot find the requested resource.
264+
'409':
265+
description: The request conflicts with the current state of the server.
266+
default:
267+
description: An unexpected error response.
268+
content:
269+
application/problem+json:
270+
schema:
271+
$ref: '#/components/schemas/Error'
272+
tags:
273+
- Clusters
274+
requestBody:
275+
required: true
276+
content:
277+
application/json:
278+
schema:
279+
$ref: '#/components/schemas/ForceDeleteRequest'
280+
security:
281+
- BearerAuth: []
243282
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools:
244283
get:
245284
operationId: getNodePoolsByClusterId
@@ -491,6 +530,51 @@ paths:
491530
$ref: '#/components/schemas/NodePoolPatchRequest'
492531
security:
493532
- BearerAuth: []
533+
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete:
534+
post:
535+
operationId: forceDeleteNodePool
536+
summary: Force-delete a nodepool
537+
description: |-
538+
Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state.
539+
This is a database-only operation. Requires a reason for audit purposes.
540+
parameters:
541+
- name: cluster_id
542+
in: path
543+
required: true
544+
description: Cluster ID
545+
schema:
546+
type: string
547+
- name: nodepool_id
548+
in: path
549+
required: true
550+
description: NodePool ID
551+
schema:
552+
type: string
553+
responses:
554+
'204':
555+
description: 'There is no content to send for this request, but the headers may be useful. '
556+
'400':
557+
description: The server could not understand the request due to invalid syntax.
558+
'404':
559+
description: The server cannot find the requested resource.
560+
'409':
561+
description: The request conflicts with the current state of the server.
562+
default:
563+
description: An unexpected error response.
564+
content:
565+
application/problem+json:
566+
schema:
567+
$ref: '#/components/schemas/Error'
568+
tags:
569+
- NodePools
570+
requestBody:
571+
required: true
572+
content:
573+
application/json:
574+
schema:
575+
$ref: '#/components/schemas/ForceDeleteRequest'
576+
security:
577+
- BearerAuth: []
494578
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses:
495579
put:
496580
operationId: putNodePoolStatuses
@@ -1279,6 +1363,17 @@ components:
12791363
$ref: '#/components/schemas/ValidationError'
12801364
description: Field-level validation errors (for validation failures)
12811365
description: RFC 9457 Problem Details error format with HyperFleet extensions
1366+
ForceDeleteRequest:
1367+
type: object
1368+
required:
1369+
- reason
1370+
properties:
1371+
reason:
1372+
type: string
1373+
minLength: 1
1374+
maxLength: 1024
1375+
description: Reason for force-deleting the resource
1376+
description: Request body for force-delete operations
12821377
NodePool:
12831378
type: object
12841379
required:

schemas/core/swagger.yaml

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ info:
1717
name: Apache 2.0
1818
url: 'https://www.apache.org/licenses/LICENSE-2.0'
1919
title: HyperFleet API
20-
version: 1.0.14
20+
version: 1.0.15
2121
host: hyperfleet.redhat.com
2222
basePath: /
2323
schemes:
@@ -291,6 +291,49 @@ paths:
291291
description: Patch a specific cluster by ID
292292
operationId: patchClusterById
293293
summary: Patch cluster by ID
294+
'/api/hyperfleet/v1/clusters/{cluster_id}/force-delete':
295+
post:
296+
consumes:
297+
- application/json
298+
produces:
299+
- application/problem+json
300+
parameters:
301+
- description: Cluster ID
302+
in: path
303+
name: cluster_id
304+
required: true
305+
type: string
306+
- in: body
307+
name: body
308+
required: true
309+
schema:
310+
$ref: '#/definitions/ForceDeleteRequest'
311+
responses:
312+
'204':
313+
description: >-
314+
There is no content to send for this request, but the headers may be
315+
useful.
316+
'400':
317+
description: The server could not understand the request due to invalid syntax.
318+
'404':
319+
description: The server cannot find the requested resource.
320+
'409':
321+
description: The request conflicts with the current state of the server.
322+
default:
323+
description: An unexpected error response.
324+
schema:
325+
$ref: '#/definitions/Error'
326+
security:
327+
- BearerAuth: []
328+
tags:
329+
- Clusters
330+
description: >-
331+
Permanently removes the cluster record from the database for a cluster
332+
stuck in Finalizing state.
333+
334+
This is a database-only operation. Requires a reason for audit purposes.
335+
operationId: forceDeleteCluster
336+
summary: Force-delete a cluster
294337
'/api/hyperfleet/v1/clusters/{cluster_id}/nodepools':
295338
get:
296339
produces:
@@ -572,6 +615,54 @@ paths:
572615
description: Patch a specific nodepool within a cluster
573616
operationId: patchNodePoolById
574617
summary: Patch nodepool by ID
618+
'/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/force-delete':
619+
post:
620+
consumes:
621+
- application/json
622+
produces:
623+
- application/problem+json
624+
parameters:
625+
- description: Cluster ID
626+
in: path
627+
name: cluster_id
628+
required: true
629+
type: string
630+
- description: NodePool ID
631+
in: path
632+
name: nodepool_id
633+
required: true
634+
type: string
635+
- in: body
636+
name: body
637+
required: true
638+
schema:
639+
$ref: '#/definitions/ForceDeleteRequest'
640+
responses:
641+
'204':
642+
description: >-
643+
There is no content to send for this request, but the headers may be
644+
useful.
645+
'400':
646+
description: The server could not understand the request due to invalid syntax.
647+
'404':
648+
description: The server cannot find the requested resource.
649+
'409':
650+
description: The request conflicts with the current state of the server.
651+
default:
652+
description: An unexpected error response.
653+
schema:
654+
$ref: '#/definitions/Error'
655+
security:
656+
- BearerAuth: []
657+
tags:
658+
- NodePools
659+
description: >-
660+
Permanently removes the nodepool record from the database for a nodepool
661+
stuck in Finalizing state.
662+
663+
This is a database-only operation. Requires a reason for audit purposes.
664+
operationId: forceDeleteNodePool
665+
summary: Force-delete a nodepool
575666
'/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses':
576667
get:
577668
produces:
@@ -1440,6 +1531,17 @@ definitions:
14401531
- title
14411532
- status
14421533
type: object
1534+
ForceDeleteRequest:
1535+
description: Request body for force-delete operations
1536+
properties:
1537+
reason:
1538+
description: Reason for force-deleting the resource
1539+
maxLength: 1024
1540+
minLength: 1
1541+
type: string
1542+
required:
1543+
- reason
1544+
type: object
14431545
NodePool:
14441546
example:
14451547
created_by: user-123@example.com

schemas/gcp/openapi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
openapi: 3.0.0
22
info:
33
title: HyperFleet API
4-
version: 1.0.14
4+
version: 1.0.15
55
contact:
66
name: HyperFleet Team
77
url: https://github.com/openshift-hyperfleet

schemas/gcp/swagger.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ info:
1717
name: Apache 2.0
1818
url: 'https://www.apache.org/licenses/LICENSE-2.0'
1919
title: HyperFleet API
20-
version: 1.0.14
20+
version: 1.0.15
2121
host: hyperfleet.redhat.com
2222
basePath: /
2323
schemes:

services/force-delete-internal.tsp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import "@typespec/http";
2+
import "@typespec/openapi";
3+
import "@typespec/openapi3";
4+
5+
import "../models/common/model.tsp";
6+
7+
using Http;
8+
using OpenAPI;
9+
10+
namespace HyperFleet;
11+
12+
@tag("Clusters")
13+
@route("/clusters")
14+
@useAuth(HyperFleet.BearerAuth)
15+
interface ClustersForceDelete {
16+
/**
17+
* Permanently removes the cluster record from the database for a cluster stuck in Finalizing state.
18+
* This is a database-only operation. Requires a reason for audit purposes.
19+
*/
20+
@route("/{cluster_id}/force-delete")
21+
@post
22+
@summary("Force-delete a cluster")
23+
@operationId("forceDeleteCluster")
24+
forceDeleteCluster(
25+
/** Cluster ID */
26+
@path cluster_id: string,
27+
@body body: ForceDeleteRequest,
28+
): {
29+
@statusCode statusCode: 204;
30+
} | Error
31+
| NotFoundResponse
32+
| BadRequestResponse
33+
| ConflictResponse;
34+
}
35+
36+
@tag("NodePools")
37+
@route("/clusters/{cluster_id}/nodepools")
38+
@useAuth(HyperFleet.BearerAuth)
39+
interface NodePoolsForceDelete {
40+
/**
41+
* Permanently removes the nodepool record from the database for a nodepool stuck in Finalizing state.
42+
* This is a database-only operation. Requires a reason for audit purposes.
43+
*/
44+
@route("/{nodepool_id}/force-delete")
45+
@post
46+
@summary("Force-delete a nodepool")
47+
@operationId("forceDeleteNodePool")
48+
forceDeleteNodePool(
49+
/** Cluster ID */
50+
@path cluster_id: string,
51+
/** NodePool ID */
52+
@path nodepool_id: string,
53+
@body body: ForceDeleteRequest,
54+
): {
55+
@statusCode statusCode: 204;
56+
} | Error
57+
| NotFoundResponse
58+
| BadRequestResponse
59+
| ConflictResponse;
60+
}

0 commit comments

Comments
 (0)