diff --git a/CHANGELOG.md b/CHANGELOG.md index 82fdd44..fdc97b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.0.13] - 2026-05-13 + +### Removed +- POST endpoints from internal status API (`/clusters/{cluster_id}/statuses` and `/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses`) + +### Changed + +- Internal status API now uses only PUT endpoints with upsert semantics for adapter status updates +- Improved documentation for PUT endpoints to clarify upsert behavior by adapter name + +## [1.0.12] - 2026-05-11 + +### Fixed + +- Aligned condition example reason/message fields with actual aggregation code output (HYPERFLEET-1017) +- Updated condition reason strings to use CamelCase format (`AllAdaptersReconciled`, `ReconciledAll`) instead of full sentences +- Updated condition message strings to match actual aggregation logic output + ## [1.0.11] - 2026-05-07 ### Added @@ -108,7 +126,9 @@ First official stable release of the HyperFleet API specification. - Interactive API documentation -[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.11...HEAD +[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.13...HEAD +[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 [1.0.11]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.10...v1.0.11 [1.0.10]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.9...v1.0.10 [1.0.9]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.8...v1.0.9 diff --git a/CLAUDE.md b/CLAUDE.md index 68c3404..3b341b0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,7 +46,7 @@ The `aliases.tsp` symlink determines which provider types are active. The `build Status endpoints are split: - `services/statuses.tsp` - GET operations (external clients) -- `services/statuses-internal.tsp` - POST operations (internal adapters only) +- `services/statuses-internal.tsp` - PUT operations (internal adapters only) The split allows generating different API contracts per audience. Only `statuses.tsp` is imported by default. diff --git a/README.md b/README.md index 45844dc..70a43f3 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Contains service definitions that generate the OpenAPI specifications: - **`services/clusters.tsp`** - Cluster resource endpoints - **`services/statuses.tsp`** - Status resource endpoints (GET only - public API) -- **`services/statuses-internal.tsp`** - Status write endpoints (POST/PUT - internal API, see below) +- **`services/statuses-internal.tsp`** - Status write endpoints (PUT - internal API, see below) - **`services/nodepools.tsp`** - NodePool resource endpoints #### Public vs Internal API Split @@ -120,7 +120,6 @@ The status endpoints are split into two files to support different API consumers | File | Operations | Audience | Included in Build | |------|------------|----------|-------------------| | `statuses.tsp` | GET (read) | External clients | ✅ Yes (default) | -| `statuses-internal.tsp` | POST (write) | Internal adapters | ❌ No (opt-in) | | `statuses-internal.tsp` | PUT (write) | Internal adapters | ❌ No (opt-in) | **Why the split?** diff --git a/main.tsp b/main.tsp index 4a3977a..2e6756f 100644 --- a/main.tsp +++ b/main.tsp @@ -20,7 +20,17 @@ using OpenAPI; * */ @service(#{ title: "HyperFleet API" }) -@info(#{ version: "1.0.12", contact: #{ name: "HyperFleet Team" }, license: #{ name: "Apache 2.0" ,url: "https://www.apache.org/licenses/LICENSE-2.0"} }) +@info(#{ + version: "1.0.13", + contact: #{ + name: "HyperFleet Team", + url: "https://github.com/openshift-hyperfleet", + }, + license: #{ + name: "Apache 2.0", + url: "https://www.apache.org/licenses/LICENSE-2.0", + }, +}) @server("https://hyperfleet.redhat.com", "Production") @route("/api/hyperfleet/v1") namespace HyperFleet; @@ -30,4 +40,3 @@ model BearerAuth { type: AuthType.http; scheme: "bearer"; } - diff --git a/models/common/model.tsp b/models/common/model.tsp index cc02bc9..ad7d15f 100644 --- a/models/common/model.tsp +++ b/models/common/model.tsp @@ -238,7 +238,7 @@ model ResourceCondition { /** * When the corresponding adapter last reported (API-managed) - * Updated every time the adapter POSTs, even if condition status hasn't changed + * Updated every time the adapter PUTs, even if condition status hasn't changed * Copied from AdapterStatus.last_report_time */ @format("date-time") last_updated_time: string; diff --git a/models/statuses/example_adapter_status.tsp b/models/statuses/example_adapter_status.tsp index 392da95..e32ebf1 100644 --- a/models/statuses/example_adapter_status.tsp +++ b/models/statuses/example_adapter_status.tsp @@ -54,7 +54,7 @@ const exampleAdapterStatus: AdapterStatus = (#{ last_report_time: "2021-01-01T10:02:00Z", }); -// Example AdapterStatusCreateRequest (for POST /clusters/{id}/statuses) +// Example AdapterStatusCreateRequest (for PUT /clusters/{id}/statuses) const exampleAdapterStatusCreateRequest: AdapterStatusCreateRequest = (#{ adapter: "validator", observed_generation: 1, diff --git a/models/statuses/model.tsp b/models/statuses/model.tsp index 2df792e..af59a44 100644 --- a/models/statuses/model.tsp +++ b/models/statuses/model.tsp @@ -85,7 +85,7 @@ model AdapterStatus { /** * When this adapter last reported its status (API-managed) - * Updated every time the adapter POSTs, even if conditions haven't changed + * Updated every time the adapter PUTs, even if conditions haven't changed * Used by Sentinel to detect adapter liveness */ @format("date-time") last_report_time: string; diff --git a/schemas/core/openapi.yaml b/schemas/core/openapi.yaml index 0a5a848..96179c6 100644 --- a/schemas/core/openapi.yaml +++ b/schemas/core/openapi.yaml @@ -1,9 +1,10 @@ openapi: 3.0.0 info: title: HyperFleet API - version: 1.0.12 + version: 1.0.13 contact: name: HyperFleet Team + url: https://github.com/openshift-hyperfleet license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0 @@ -507,55 +508,10 @@ paths: security: - BearerAuth: [] /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses: - post: - operationId: postNodePoolStatuses - summary: Create or update adapter status - description: |- - Adapter creates or updates its status report for this nodepool. - If adapter already has a status, it will be updated (upsert by adapter name). - - Response includes the full adapter status with all conditions. - Adapter should call this endpoint every time it evaluates the nodepool. - 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: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/AdapterStatus' - '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. - tags: - - NodePool statuses - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdapterStatusCreateRequest' - security: - - BearerAuth: [] put: operationId: putNodePoolStatuses - summary: Adapter creates or updates resource nodepool status - description: Idempotent upsert of an adapter status for this nodepool. Use instead of POST when the adapter name is known upfront. + summary: Adapter creates or updates nodepool status + description: Adapters call this endpoint to report the status for a nodepool after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name). This allows HyperFleet to aggregate multiple adapter perspectives into a unified nodepool status. parameters: - name: cluster_id in: path @@ -566,7 +522,7 @@ paths: - name: nodepool_id in: path required: true - description: Nodepool ID + description: NodePool ID schema: type: string responses: @@ -631,49 +587,10 @@ paths: tags: - NodePool statuses /api/hyperfleet/v1/clusters/{cluster_id}/statuses: - post: - operationId: postClusterStatuses - summary: Create or update adapter status - description: |- - Adapter creates or updates its status report for this cluster. - If adapter already has a status, it will be updated (upsert by adapter name). - - Response includes the full adapter status with all conditions. - Adapter should call this endpoint every time it evaluates the cluster. - parameters: - - name: cluster_id - in: path - required: true - description: Cluster ID - schema: - type: string - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/AdapterStatus' - '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. - tags: - - Cluster statuses - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdapterStatusCreateRequest' - security: - - BearerAuth: [] put: operationId: putClusterStatuses - summary: Adapter creates or updates resource cluster status - description: Idempotent upsert of an adapter status for this cluster. Use instead of POST when the adapter name is known upfront. + summary: Adapter creates or updates cluster status + description: Adapters call this endpoint to report the status for a cluster after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name). This allows HyperFleet to aggregate multiple adapter perspectives into a unified cluster status. parameters: - name: cluster_id in: path @@ -902,7 +819,7 @@ components: format: date-time description: |- When this adapter last reported its status (API-managed) - Updated every time the adapter POSTs, even if conditions haven't changed + Updated every time the adapter PUTs, even if conditions haven't changed Used by Sentinel to detect adapter liveness description: |- AdapterStatus represents the complete status report from an adapter @@ -1742,7 +1659,7 @@ components: format: date-time description: |- When the corresponding adapter last reported (API-managed) - Updated every time the adapter POSTs, even if condition status hasn't changed + Updated every time the adapter PUTs, even if condition status hasn't changed Copied from AdapterStatus.last_report_time description: |- Condition in Cluster/NodePool status diff --git a/schemas/core/swagger.yaml b/schemas/core/swagger.yaml index 3d7433e..c4804b6 100644 --- a/schemas/core/swagger.yaml +++ b/schemas/core/swagger.yaml @@ -2,6 +2,7 @@ swagger: '2.0' info: contact: name: HyperFleet Team + url: 'https://github.com/openshift-hyperfleet' description: >- HyperFleet API provides simple CRUD operations for managing cluster resources and their status history. @@ -16,7 +17,7 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' title: HyperFleet API - version: 1.0.12 + version: 1.0.13 host: hyperfleet.redhat.com basePath: / schemes: @@ -655,56 +656,6 @@ paths: description: Returns adapter status reports for this nodepool operationId: getNodePoolsStatuses summary: List all adapter statuses for nodepools - post: - consumes: - - application/json - produces: - - application/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/AdapterStatusCreateRequest' - responses: - '201': - description: >- - The request has succeeded and a new resource has been created as a - result. - schema: - $ref: '#/definitions/AdapterStatus' - '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. - security: - - BearerAuth: [] - tags: - - NodePool statuses - description: >- - Adapter creates or updates its status report for this nodepool. - - If adapter already has a status, it will be updated (upsert by adapter - name). - - - Response includes the full adapter status with all conditions. - - Adapter should call this endpoint every time it evaluates the nodepool. - operationId: postNodePoolStatuses - summary: Create or update adapter status put: consumes: - application/json @@ -716,7 +667,7 @@ paths: name: cluster_id required: true type: string - - description: Nodepool ID + - description: NodePool ID in: path name: nodepool_id required: true @@ -744,10 +695,13 @@ paths: tags: - NodePool statuses description: >- - Idempotent upsert of an adapter status for this nodepool. Use instead of - POST when the adapter name is known upfront. + Adapters call this endpoint to report the status for a nodepool after + each evaluation. The adapter's status entry is created if it doesn't + exist, or updated if it does (upserted by adapter name). This allows + HyperFleet to aggregate multiple adapter perspectives into a unified + nodepool status. operationId: putNodePoolStatuses - summary: Adapter creates or updates resource nodepool status + summary: Adapter creates or updates nodepool status '/api/hyperfleet/v1/clusters/{cluster_id}/statuses': get: produces: @@ -807,51 +761,6 @@ paths: description: Returns adapter status reports for this cluster operationId: getClusterStatuses summary: List all adapter statuses for cluster - post: - consumes: - - application/json - produces: - - application/json - parameters: - - description: Cluster ID - in: path - name: cluster_id - required: true - type: string - - in: body - name: body - required: true - schema: - $ref: '#/definitions/AdapterStatusCreateRequest' - responses: - '201': - description: >- - The request has succeeded and a new resource has been created as a - result. - schema: - $ref: '#/definitions/AdapterStatus' - '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. - security: - - BearerAuth: [] - tags: - - Cluster statuses - description: >- - Adapter creates or updates its status report for this cluster. - - If adapter already has a status, it will be updated (upsert by adapter - name). - - - Response includes the full adapter status with all conditions. - - Adapter should call this endpoint every time it evaluates the cluster. - operationId: postClusterStatuses - summary: Create or update adapter status put: consumes: - application/json @@ -886,10 +795,13 @@ paths: tags: - Cluster statuses description: >- - Idempotent upsert of an adapter status for this cluster. Use instead of - POST when the adapter name is known upfront. + Adapters call this endpoint to report the status for a cluster after + each evaluation. The adapter's status entry is created if it doesn't + exist, or updated if it does (upserted by adapter name). This allows + HyperFleet to aggregate multiple adapter perspectives into a unified + cluster status. operationId: putClusterStatuses - summary: Adapter creates or updates resource cluster status + summary: Adapter creates or updates cluster status /api/hyperfleet/v1/nodepools: get: produces: @@ -1056,7 +968,7 @@ definitions: description: >- When this adapter last reported its status (API-managed) - Updated every time the adapter POSTs, even if conditions haven't + Updated every time the adapter PUTs, even if conditions haven't changed Used by Sentinel to detect adapter liveness @@ -1928,7 +1840,7 @@ definitions: description: >- When the corresponding adapter last reported (API-managed) - Updated every time the adapter POSTs, even if condition status hasn't + Updated every time the adapter PUTs, even if condition status hasn't changed Copied from AdapterStatus.last_report_time diff --git a/schemas/gcp/openapi.yaml b/schemas/gcp/openapi.yaml index 0a885d3..f44ab5d 100644 --- a/schemas/gcp/openapi.yaml +++ b/schemas/gcp/openapi.yaml @@ -1,9 +1,10 @@ openapi: 3.0.0 info: title: HyperFleet API - version: 1.0.12 + version: 1.0.13 contact: name: HyperFleet Team + url: https://github.com/openshift-hyperfleet license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0 @@ -793,7 +794,7 @@ components: format: date-time description: |- When this adapter last reported its status (API-managed) - Updated every time the adapter POSTs, even if conditions haven't changed + Updated every time the adapter PUTs, even if conditions haven't changed Used by Sentinel to detect adapter liveness description: |- AdapterStatus represents the complete status report from an adapter @@ -1826,7 +1827,7 @@ components: format: date-time description: |- When the corresponding adapter last reported (API-managed) - Updated every time the adapter POSTs, even if condition status hasn't changed + Updated every time the adapter PUTs, even if condition status hasn't changed Copied from AdapterStatus.last_report_time description: |- Condition in Cluster/NodePool status diff --git a/schemas/gcp/swagger.yaml b/schemas/gcp/swagger.yaml index 44a8688..c72a1ad 100644 --- a/schemas/gcp/swagger.yaml +++ b/schemas/gcp/swagger.yaml @@ -2,6 +2,7 @@ swagger: '2.0' info: contact: name: HyperFleet Team + url: 'https://github.com/openshift-hyperfleet' description: >- HyperFleet API provides simple CRUD operations for managing cluster resources and their status history. @@ -16,7 +17,7 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' title: HyperFleet API - version: 1.0.12 + version: 1.0.13 host: hyperfleet.redhat.com basePath: / schemes: @@ -930,7 +931,7 @@ definitions: description: >- When this adapter last reported its status (API-managed) - Updated every time the adapter POSTs, even if conditions haven't + Updated every time the adapter PUTs, even if conditions haven't changed Used by Sentinel to detect adapter liveness @@ -1993,7 +1994,7 @@ definitions: description: >- When the corresponding adapter last reported (API-managed) - Updated every time the adapter POSTs, even if condition status hasn't + Updated every time the adapter PUTs, even if condition status hasn't changed Copied from AdapterStatus.last_report_time diff --git a/services/statuses-internal.tsp b/services/statuses-internal.tsp index d74eff3..5f5b999 100644 --- a/services/statuses-internal.tsp +++ b/services/statuses-internal.tsp @@ -15,35 +15,11 @@ namespace HyperFleet; @useAuth(HyperFleet.BearerAuth) @tag("Cluster statuses") interface ClusterStatusesInternal { - /** - * Adapter creates or updates its status report for this cluster. - * If adapter already has a status, it will be updated (upsert by adapter name). - * - * Response includes the full adapter status with all conditions. - * Adapter should call this endpoint every time it evaluates the cluster. - */ - @route("") - @post - @summary("Create or update adapter status") - @operationId("postClusterStatuses") - postClusterStatuses( - /** - * Cluster ID - */ - @path cluster_id: string, - - @body body: AdapterStatusCreateRequest, - ): - | (CreatedResponse & AdapterStatus) - | BadRequestResponse - | NotFoundResponse - | ConflictResponse; - @route("") @put - @summary("Adapter creates or updates resource cluster status") + @summary("Adapter creates or updates cluster status") @operationId("putClusterStatuses") - @doc("Idempotent upsert of an adapter status for this cluster. Use instead of POST when the adapter name is known upfront.") + @doc("Adapters call this endpoint to report the status for a cluster after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name). This allows HyperFleet to aggregate multiple adapter perspectives into a unified cluster status.") putClusterStatuses( /** * Cluster ID @@ -62,40 +38,11 @@ interface ClusterStatusesInternal { @route("/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses") @useAuth(HyperFleet.BearerAuth) interface NodePoolStatusesInternal { - /** - * Adapter creates or updates its status report for this nodepool. - * If adapter already has a status, it will be updated (upsert by adapter name). - * - * Response includes the full adapter status with all conditions. - * Adapter should call this endpoint every time it evaluates the nodepool. - */ - @route("") - @post - @summary("Create or update adapter status") - @operationId("postNodePoolStatuses") - postNodePoolStatuses( - /** - * Cluster ID - */ - @path cluster_id: string, - - /** - * Nodepool ID - */ - @path nodepool_id: string, - - @body body: AdapterStatusCreateRequest, - ): - | (CreatedResponse & AdapterStatus) - | BadRequestResponse - | NotFoundResponse - | ConflictResponse; - @route("") @put - @summary("Adapter creates or updates resource nodepool status") + @summary("Adapter creates or updates nodepool status") @operationId("putNodePoolStatuses") - @doc("Idempotent upsert of an adapter status for this nodepool. Use instead of POST when the adapter name is known upfront.") + @doc("Adapters call this endpoint to report the status for a nodepool after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name). This allows HyperFleet to aggregate multiple adapter perspectives into a unified nodepool status.") putNodePoolStatuses( /** * Cluster ID @@ -103,7 +50,7 @@ interface NodePoolStatusesInternal { @path cluster_id: string, /** - * Nodepool ID + * NodePool ID */ @path nodepool_id: string,