Skip to content

Commit 964de33

Browse files
committed
HYPERFLEET-1017 - refactor: rename aggregated Available condition to LastKnownReconciled
Rename the API-computed aggregated condition from Available to LastKnownReconciled. The adapter-level Available condition remains unchanged. Includes backward compatibility for legacy DB records, differentiated reason/message for false conditions, minItems: 3 enforcement in OpenAPI schemas, and updated documentation.
1 parent 60956b9 commit 964de33

11 files changed

Lines changed: 192 additions & 105 deletions

docs/api-operator-guide.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ A practical guide for deploying, configuring, and operating the HyperFleet API c
1717
- [Rules to accept/discard adapter reports](#rules-to-acceptdiscard-adapter-reports)
1818
- [Computing `observed_generation`](#computing-observed_generation)
1919
- [Computing `status.conditions[type==Ready].last_updated_time`](#computing-statusconditionstypereadylast_updated_time)
20-
- [Computing `status.conditions[type==Available].last_updated_time`](#computing-statusconditionstypeavailablelast_updated_time)
21-
- [Computing `last_transition_time` for both `Ready` and `Available`](#computing-last_transition_time-for-both-ready-and-available)
20+
- [Computing `status.conditions[type==LastKnownReconciled].last_updated_time`](#computing-statusconditionstypelastknownreconciledlast_updated_time)
21+
- [Computing `last_transition_time` for both `Ready` and `LastKnownReconciled`](#computing-last_transition_time-for-both-ready-and-lastknownreconciled)
2222
3. [Configuration Reference](#3-configuration-reference)
2323
- [Adapter Requirements (REQUIRED)](#31-adapter-requirements-required)
2424
- [Database Configuration](#32-database-configuration)
@@ -108,7 +108,7 @@ Resource (e.g., Cluster)
108108

109109
2. **Automatic Version Tracking (generation)**: Every time you update the `spec`, the API automatically increments the `generation` counter. This allows distributed adapters to detect when they need to reconcile infrastructure changes.
110110

111-
3. **Observed State (status)**: Adapters report their progress and results back to the API via status endpoints. The API aggregates these reports into unified resource-level conditions (e.g., `Ready`, `Available`).
111+
3. **Observed State (status)**: Adapters report their progress and results back to the API via status endpoints. The API aggregates these reports into unified resource-level conditions (e.g., `Ready`, `LastKnownReconciled`).
112112

113113
4. **Filtering (labels)**: Labels are key-value pairs you can attach to resources for organization and filtering (e.g., `environment: production`, `region: us-east-1`). E.g., Sentinel instances can define resource selectors based on labels to watch specific subsets of resources, enabling horizontal scaling across multiple Sentinel deployments.
114114

@@ -147,7 +147,13 @@ GET /api/hyperfleet/v1/clusters/{id}
147147
"status": {
148148
"conditions": [
149149
{
150-
"type": "Available",
150+
"type": "Reconciled",
151+
"status": "True",
152+
"observed_generation": 1,
153+
"last_transition_time": "2026-03-10T07:56:35Z"
154+
},
155+
{
156+
"type": "LastKnownReconciled",
151157
"status": "True",
152158
"observed_generation": 1,
153159
"last_transition_time": "2026-03-10T07:56:35Z"
@@ -164,7 +170,7 @@ GET /api/hyperfleet/v1/clusters/{id}
164170
"updated_time": "2026-03-10T07:56:35Z"
165171
}
166172

167-
→ API returns aggregated status with Available and Ready conditions
173+
→ API returns aggregated status with Reconciled, LastKnownReconciled, and Ready conditions
168174

169175
# 3. View adapter statuses
170176
GET /api/hyperfleet/v1/clusters/{id}/statuses
@@ -323,7 +329,7 @@ HyperFleet API aggregates the condition values reported by adapters associated w
323329
| Condition | Meaning | When True |
324330
|-----------|---------|-----------|
325331
| **Ready** | Resource is fully reconciled at current spec | All registered adapters report `Available=True` at the **current** `resource.spec.generation` |
326-
| **Available** | Resource is operational at any known good configuration | All registered adapters report `Available=True` (at any generation) |
332+
| **LastKnownReconciled** | Resource is operational at any known good configuration | All registered adapters report `Available=True` (at any generation) |
327333

328334
**Note**: The meaning of the field `last_updated_time` for the aggregated conditions has special meaning. It doesn't reflect the last time it was updated from adapters but the OLDEST time it can be considered to be valid.
329335

@@ -337,16 +343,16 @@ The resource `status.conditions` array contains:
337343
- `True`: All required adapters `conditions[type=Available].status==True` at current spec generation
338344
- `False`: Any other combination of conditions
339345

340-
- **Available** - The resource is reconciled at a generation of the spec, current or past
346+
- **LastKnownReconciled** - The resource is reconciled at a generation of the spec, current or past
341347
- This condition is stateful meaning that is computed taking into account its previous values of `status` and `observed_generation`
342348
- This condition is "best effort", since there are cases that can not be covered correctly.
343349
- `True`:
344350
- All required adapters `conditions[type=Available].status==True` for the same `observed_generation`
345351
- Current value `status==True` and required adapters `conditions[type=Available]` at mixed `observed_generation`
346352
- `False`: Any other combination of conditions
347-
- e.g. `Available=True` for `observed_generation==1`
348-
- One adapter reports `Available=False` for `observed_generation=1` `Available` transitions to `False`
349-
- One adapter reports `Available=False` for `observed_generation=2` `Available` keeps its `True` status
353+
- e.g. `LastKnownReconciled=True` for `observed_generation==1`
354+
- One adapter reports `Available=False` for `observed_generation=1` `LastKnownReconciled` transitions to `False`
355+
- One adapter reports `Available=False` for `observed_generation=2` `LastKnownReconciled` keeps its `True` status
350356

351357
- One **per-adapter** condition for each required adapter that has reported, mirroring the adapter's `conditions[type=Available]`:
352358
- `type`: Derived from the adapter name — PascalCase with `Successful` suffix (e.g., `adapter1` → `Adapter1Successful`, `my-adapter` → `MyAdapterSuccessful`)
@@ -392,7 +398,7 @@ These are API examples for a resource and resource statuses:
392398
"last_transition_time": "2021-01-01T10:00:00Z"
393399
},
394400
{
395-
"type": "Available",
401+
"type": "LastKnownReconciled",
396402
"status": "True",
397403
"reason": "All adapters reported Available True for the same generation",
398404
"message": "All adapters reported Available True for the same generation",
@@ -497,23 +503,23 @@ These are API examples for a resource and resource statuses:
497503
When a resource is created:
498504

499505
- Initial `generation` is 1 and aggregated conditions are evaluated
500-
- `observed_generation` for `Ready` and `Available` aggregated conditions is 1
501-
- `last_updated_time` and `last_transition_time` for `Ready` and `Available` aggregated conditions is `resource.last_updated_time`
506+
- `observed_generation` for `Ready` and `LastKnownReconciled` aggregated conditions is 1
507+
- `last_updated_time` and `last_transition_time` for `Ready` and `LastKnownReconciled` aggregated conditions is `resource.last_updated_time`
502508

503509
When a resource is changed:
504510

505511
- `resource.generation` gets incremented and aggregated conditions are re-evaluated
506512
- `status.conditions[type==Ready].observed_generation` always follows `resource.generation`
507-
- `status.conditions[type==Available].observed_generation` changes when all required adapters `condition[type==Available].observed_generation==resource.generation` otherwise remains unchanged.
513+
- `status.conditions[type==LastKnownReconciled].observed_generation` changes when all required adapters `condition[type==Available].observed_generation==resource.generation` otherwise remains unchanged.
508514

509515
##### Computing `observed_generation`
510516

511517
- For `Ready` it always matches `resource.generation`
512-
- For `Available`:
518+
- For `LastKnownReconciled`:
513519
- If all required adapters have a common `observed_generation` it will match the common value
514520
- If required adapters have mixed `observed_generation`
515-
- If `Available` is `True`, `observed_generation` remains at its current value
516-
- If `Available` is `False`, `observed_generation` will get the value of the `max(condition[type==Available].observed_generation)`
521+
- If `LastKnownReconciled` is `True`, `observed_generation` remains at its current value
522+
- If `LastKnownReconciled` is `False`, `observed_generation` will get the value of the `max(condition[type==Available].observed_generation)`
517523

518524
##### Computing `status.conditions[type==Ready].last_updated_time`
519525

@@ -526,14 +532,14 @@ The meaning of `last_updated_time` in the aggregated conditions refers to the ne
526532
- Why do we want to keep the "oldest" value? because if it is too old, we need to trigger a reconciliation
527533
- When some required adapter conditions `condition[type==Available].observed_generation==resource.generation` then `last_updated_time=min(statuses[].conditions[type==Available && observed_generation==resource.generation].observed_time)`
528534

529-
##### Computing `status.conditions[type==Available].last_updated_time`
535+
##### Computing `status.conditions[type==LastKnownReconciled].last_updated_time`
530536

531537
- If all required adapters have `condition[type==Available].observed_generation` at the same value then `last_updated_time=min(statuses[].conditions[type==Available].observed_time)`
532538
- If not all required adapters have `condition[type==Available].observed_generation` at the same value:
533539
- If any adapter at current `observed_generation==X` has `conditions[type==Available].status==False` then `last_updated_time=min(adapters[type==Available && observed_generation==X].observed_time`
534540
- In any other case `last_updated_time` is kept unchanged
535541

536-
##### Computing `last_transition_time` for both `Ready` and `Available`
542+
##### Computing `last_transition_time` for both `Ready` and `LastKnownReconciled`
537543

538544
- Meaning is last time this condition’s status (True / False) changed, regardless of the existing and new `observed_generation`
539545
- This property is stateful since it relies on the existing value to determine if there has been a transition

docs/api-resources.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
5353
"status": {
5454
"conditions": [
5555
{
56-
"type": "Available",
56+
"type": "LastKnownReconciled",
5757
"status": "False",
5858
"reason": "AwaitingAdapters",
5959
"message": "Waiting for adapters to report status",
@@ -79,7 +79,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
7979

8080
</details>
8181

82-
**Note**: Status initially has `Available=False` and `Ready=False` conditions until adapters report status.
82+
**Note**: Status initially has `LastKnownReconciled=False` and `Ready=False` conditions until adapters report status.
8383

8484
### Get Cluster
8585

@@ -108,7 +108,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
108108
"status": {
109109
"conditions": [
110110
{
111-
"type": "Available",
111+
"type": "LastKnownReconciled",
112112
"status": "True",
113113
"reason": "ResourceAvailable",
114114
"message": "Cluster is accessible",
@@ -304,7 +304,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses
304304
"status": {
305305
"conditions": [
306306
{
307-
"type": "Available",
307+
"type": "LastKnownReconciled",
308308
"status": "False",
309309
"reason": "AwaitingAdapters",
310310
"message": "Waiting for adapters to report status",
@@ -361,7 +361,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses
361361
"status": {
362362
"conditions": [
363363
{
364-
"type": "Available",
364+
"type": "LastKnownReconciled",
365365
"status": "True",
366366
"reason": "ResourceAvailable",
367367
"message": "NodePool is accessible",
@@ -446,7 +446,7 @@ See **[search.md](search.md)** for complete documentation.
446446
The status object contains synthesized conditions computed from adapter reports:
447447

448448
- `conditions` - Array of resource conditions, including:
449-
- **Available** - Whether resource is running at any known good configuration
449+
- **LastKnownReconciled** - Whether resource is running at any known good configuration
450450
- **Ready** - Whether all adapters have processed current spec generation
451451
- Additional conditions from adapters (with `observed_generation`, timestamps)
452452

docs/search.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ Label keys must contain only lowercase letters (a-z), digits (0-9), and undersco
9898

9999
Query resources by status conditions: `status.conditions.<Type>='<Status>'`
100100

101-
Condition types must be PascalCase (`Ready`, `Available`) and status must be `True` or `False` for resource conditions.
101+
Condition types must be PascalCase (`Ready`, `LastKnownReconciled`) and status must be `True` or `False` for resource conditions.
102102

103103
**Note:** Only the `=` operator is supported for condition queries. Other operators (`!=`, `<`, `>`, `in`, etc.) will return an error. The `NOT` operator is not supported with condition queries (`status.conditions.<Type>` or `status.conditions.<Type>.<Subfield>`) and will return a `400 Bad Request` error. Use the inverse condition value instead (e.g., `status.conditions.Ready='False'` rather than `NOT status.conditions.Ready='True'`).
104104

105105
```bash
106106
# Find available clusters
107107
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
108-
--data-urlencode "search=status.conditions.Available='True'"
108+
--data-urlencode "search=status.conditions.LastKnownReconciled='True'"
109109

110110
# Find clusters that are not ready
111111
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \

openapi/openapi.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ paths:
5050
5151
**Note**: The `status` object in the response is read-only and computed by the service.
5252
It is NOT part of the request body. Initially,
53-
status.conditions will include mandatory "Available", "Ready" and "Reconciled" conditions.
53+
status.conditions will include mandatory "LastKnownReconciled", "Ready" and "Reconciled" conditions.
5454
parameters: []
5555
responses:
5656
'201':
@@ -189,7 +189,7 @@ paths:
189189
created_time: '2021-01-01T10:00:00Z'
190190
last_updated_time: '2021-01-01T10:00:00Z'
191191
last_transition_time: '2021-01-01T10:00:00Z'
192-
- type: Available
192+
- type: LastKnownReconciled
193193
status: 'True'
194194
reason: All adapters reported Available True for the same generation
195195
message: All adapters reported Available True for the same generation
@@ -394,7 +394,7 @@ paths:
394394
created_time: '2021-01-01T10:00:00Z'
395395
last_updated_time: '2021-01-01T10:00:00Z'
396396
last_transition_time: '2021-01-01T10:00:00Z'
397-
- type: Available
397+
- type: LastKnownReconciled
398398
status: 'True'
399399
reason: All adapters reported Available True for the same generation
400400
message: All adapters reported Available True for the same generation
@@ -1075,7 +1075,7 @@ components:
10751075
created_time: '2021-01-01T10:00:00Z'
10761076
last_updated_time: '2021-01-01T10:00:00Z'
10771077
last_transition_time: '2021-01-01T10:00:00Z'
1078-
- type: Available
1078+
- type: LastKnownReconciled
10791079
status: 'True'
10801080
reason: All adapters reported Available True for the same generation
10811081
message: All adapters reported Available True for the same generation
@@ -1192,14 +1192,14 @@ components:
11921192
type: array
11931193
items:
11941194
$ref: '#/components/schemas/ResourceCondition'
1195-
minItems: 2
1195+
minItems: 3
11961196
description: |-
11971197
List of status conditions for the cluster.
11981198
1199-
**Mandatory conditions**:
1199+
**Mandatory conditions**:
12001200
- `type: "Ready"` *(deprecated — use Reconciled)*: Whether all adapters report successfully at the current generation.
12011201
- `type: "Reconciled"`: Whether the resource's desired state has been fully reconciled by all adapters at the current generation.
1202-
- `type: "Available"`: Aggregated adapter result for a common observed_generation.
1202+
- `type: "LastKnownReconciled"`: Aggregated adapter result for a common observed_generation. Sticky — stays True as long as all required adapters were reconciled at a common observed generation, even if a new generation is being processed.
12031203
12041204
These conditions are present immediately upon resource creation.
12051205
description: |-
@@ -1374,7 +1374,7 @@ components:
13741374
created_time: '2021-01-01T10:00:00Z'
13751375
last_updated_time: '2021-01-01T10:00:00Z'
13761376
last_transition_time: '2021-01-01T10:00:00Z'
1377-
- type: Available
1377+
- type: LastKnownReconciled
13781378
status: 'True'
13791379
reason: All adapters reported Available True for the same generation
13801380
message: All adapters reported Available True for the same generation
@@ -1558,14 +1558,14 @@ components:
15581558
type: array
15591559
items:
15601560
$ref: '#/components/schemas/ResourceCondition'
1561-
minItems: 2
1561+
minItems: 3
15621562
description: |-
15631563
List of status conditions for the nodepool.
15641564
1565-
**Mandatory conditions**:
1565+
**Mandatory conditions**:
15661566
- `type: "Ready"` *(deprecated — use Reconciled)*: Whether all adapters report successfully at the current generation.
15671567
- `type: "Reconciled"`: Whether the resource's desired state has been fully reconciled by all adapters at the current generation.
1568-
- `type: "Available"`: Aggregated adapter result for a common observed_generation.
1568+
- `type: "LastKnownReconciled"`: Aggregated adapter result for a common observed_generation. Sticky — stays True as long as all required adapters were reconciled at a common observed generation, even if a new generation is being processed.
15691569
15701570
These conditions are present immediately upon resource creation.
15711571
description: |-

pkg/api/status_types.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ func (s AdapterConditionStatus) IsValid() bool {
2828

2929
// Condition type constants
3030
const (
31-
ConditionTypeAvailable = "Available"
32-
ConditionTypeApplied = "Applied"
33-
ConditionTypeHealth = "Health"
34-
ConditionTypeReady = "Ready"
35-
ConditionTypeReconciled = "Reconciled"
36-
ConditionTypeFinalized = "Finalized"
31+
ConditionTypeAvailable = "Available"
32+
ConditionTypeLastKnownReconciled = "LastKnownReconciled"
33+
ConditionTypeApplied = "Applied"
34+
ConditionTypeHealth = "Health"
35+
ConditionTypeReady = "Ready"
36+
ConditionTypeReconciled = "Reconciled"
37+
ConditionTypeFinalized = "Finalized"
3738
)
3839

3940
// ResourceCondition represents a condition of a resource

0 commit comments

Comments
 (0)