You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
HYPERFLEET-1017 - refactor: rename aggregated Available condition to LastKnownReconciled
- Rename the aggregated Available condition to LastKnownReconciled
across service layer, tests, docs, and OpenAPI spec
- Sync OpenAPI spec from upstream hyperfleet-api-spec v1.0.11
(includes new PUT endpoints for cluster/nodepool statuses)
- Rename Reconciled condition reason constants from Ready* prefix
to Reconciled* prefix to match their actual usage
- Fix doc examples: correct Reconciled condition reason values
to match code constants (ReconciledAll, AllAdaptersReconciled)
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.
110
110
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 (`Reconciled`, `LastKnownReconciled`, and `Ready` — which is a deprecated alias of `Reconciled`).
112
112
113
113
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.
114
114
@@ -147,7 +147,13 @@ GET /api/hyperfleet/v1/clusters/{id}
147
147
"status": {
148
148
"conditions": [
149
149
{
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",
151
157
"status": "True",
152
158
"observed_generation": 1,
153
159
"last_transition_time": "2026-03-10T07:56:35Z"
@@ -164,7 +170,7 @@ GET /api/hyperfleet/v1/clusters/{id}
164
170
"updated_time": "2026-03-10T07:56:35Z"
165
171
}
166
172
167
-
→ API returns aggregated status with Available and Ready conditions
173
+
→ API returns aggregated status with Reconciled, LastKnownReconciled, and Ready conditions
168
174
169
175
# 3. View adapter statuses
170
176
GET /api/hyperfleet/v1/clusters/{id}/statuses
@@ -318,12 +324,13 @@ adapters:
318
324
319
325
### 2.3 Status Aggregation
320
326
321
-
HyperFleet API aggregates the condition values reported by adapters associated with a resource and adds two synthetic aggregated conditions.
327
+
HyperFleet API aggregates the condition values reported by adapters associated with a resource and adds three synthetic aggregated conditions.
322
328
323
329
| Condition | Meaning | When True |
324
330
|-----------|---------|-----------|
325
-
| **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) |
331
+
| **Reconciled** | Resource is fully reconciled at current spec | All registered adapters report `Available=True` at the **current** `resource.spec.generation` |
332
+
| **LastKnownReconciled** | Resource is operational at any known good configuration | All registered adapters report `Available=True` for a common `observed_generation`, or sticky-true is preserved when adapters are transitioning to a new generation |
333
+
| **Ready** *(deprecated — use Reconciled)* | Alias of Reconciled | Same as Reconciled |
327
334
328
335
**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.
329
336
@@ -333,20 +340,22 @@ This is used by other components (Sentinel) to determine how "fresh" the status
333
340
334
341
The resource `status.conditions` array contains:
335
342
336
-
- **Ready** - The resource is reconciled to the latest resource spec
343
+
- **Reconciled** - The resource is reconciled to the latest resource spec
337
344
- `True`: All required adapters `conditions[type=Available].status==True` at current spec generation
338
345
- `False`: Any other combination of conditions
339
346
340
-
- **Available** - The resource is reconciled at a generation of the spec, current or past
347
+
- **Ready** *(deprecated — use Reconciled)* - Alias of Reconciled with identical semantics
348
+
349
+
- **LastKnownReconciled** - The resource is reconciled at a generation of the spec, current or past
341
350
- This condition is stateful meaning that is computed taking into account its previous values of `status` and `observed_generation`
342
351
- This condition is "best effort", since there are cases that can not be covered correctly.
343
352
- `True`:
344
353
- All required adapters `conditions[type=Available].status==True` for the same `observed_generation`
345
354
- Current value `status==True` and required adapters `conditions[type=Available]` at mixed `observed_generation`
346
355
- `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
356
+
- e.g. `LastKnownReconciled=True` for `observed_generation==1`
357
+
- One adapter reports `Available=False` for `observed_generation=1` `LastKnownReconciled` transitions to `False`
358
+
- One adapter reports `Available=False` for `observed_generation=2` `LastKnownReconciled` keeps its `True` status
350
359
351
360
- One **per-adapter** condition for each required adapter that has reported, mirroring the adapter's `conditions[type=Available]`:
352
361
- `type`: Derived from the adapter name — PascalCase with `Successful` suffix (e.g., `adapter1` → `Adapter1Successful`, `my-adapter` → `MyAdapterSuccessful`)
@@ -392,10 +401,10 @@ These are API examples for a resource and resource statuses:
392
401
"last_transition_time": "2021-01-01T10:00:00Z"
393
402
},
394
403
{
395
-
"type": "Available",
404
+
"type": "LastKnownReconciled",
396
405
"status": "True",
397
-
"reason": "All adapters reported Available True for the same generation",
398
-
"message": "All adapters reported AvailableTrue for the same generation",
406
+
"reason": "AllAdaptersReconciled",
407
+
"message": "All required adapters report Available=True for the tracked generation",
399
408
"observed_generation": 1,
400
409
"created_time": "2021-01-01T10:00:00Z",
401
410
"last_updated_time": "2021-01-01T10:00:00Z",
@@ -497,23 +506,23 @@ These are API examples for a resource and resource statuses:
497
506
When a resource is created:
498
507
499
508
- 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`
509
+
- `observed_generation`for `Ready` and `LastKnownReconciled` aggregated conditions is 1
510
+
- `last_updated_time`and `last_transition_time` for `Ready` and `LastKnownReconciled` aggregated conditions is `resource.last_updated_time`
502
511
503
512
When a resource is changed:
504
513
505
514
- `resource.generation`gets incremented and aggregated conditions are re-evaluated
- `status.conditions[type==Available].observed_generation`changes when all required adapters `condition[type==Available].observed_generation==resource.generation` otherwise remains unchanged.
516
+
- `status.conditions[type==LastKnownReconciled].observed_generation`changes when all required adapters `condition[type==Available].observed_generation==resource.generation` otherwise remains unchanged.
508
517
509
518
##### Computing `observed_generation`
510
519
511
520
- For `Ready` it always matches `resource.generation`
512
-
- For `Available`:
521
+
- For `LastKnownReconciled`:
513
522
- If all required adapters have a common `observed_generation` it will match the common value
514
523
- 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)`
524
+
- If `LastKnownReconciled` is `True`, `observed_generation` remains at its current value
525
+
- If `LastKnownReconciled` is `False`, `observed_generation` will get the value of the `max(condition[type==Available].observed_generation)`
@@ -526,14 +535,14 @@ The meaning of `last_updated_time` in the aggregated conditions refers to the ne
526
535
- Why do we want to keep the "oldest" value? because if it is too old, we need to trigger a reconciliation
527
536
- 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)`
- 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)`
532
541
- If not all required adapters have `condition[type==Available].observed_generation` at the same value:
533
542
- 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`
534
543
- In any other case `last_updated_time` is kept unchanged
535
544
536
-
##### Computing `last_transition_time` for both `Ready` and `Available`
545
+
##### Computing `last_transition_time` for both `Ready` and `LastKnownReconciled`
537
546
538
547
- Meaning is last time this condition’s status (True / False) changed, regardless of the existing and new `observed_generation`
539
548
- This property is stateful since it relies on the existing value to determine if there has been a transition
Copy file name to clipboardExpand all lines: docs/api-resources.md
+50-12Lines changed: 50 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,7 +53,17 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
53
53
"status": {
54
54
"conditions": [
55
55
{
56
-
"type": "Available",
56
+
"type": "Reconciled",
57
+
"status": "False",
58
+
"reason": "AwaitingAdapters",
59
+
"message": "Waiting for adapters to report status",
60
+
"observed_generation": 1,
61
+
"created_time": "2025-01-01T00:00:00Z",
62
+
"last_updated_time": "2025-01-01T00:00:00Z",
63
+
"last_transition_time": "2025-01-01T00:00:00Z"
64
+
},
65
+
{
66
+
"type": "LastKnownReconciled",
57
67
"status": "False",
58
68
"reason": "AwaitingAdapters",
59
69
"message": "Waiting for adapters to report status",
@@ -79,7 +89,7 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
79
89
80
90
</details>
81
91
82
-
**Note**: Status initially has `Available=False` and `Ready=False` conditions until adapters report status.
92
+
**Note**: Status initially has `Reconciled=False`, `LastKnownReconciled=False`, and `Ready=False` conditions until adapters report status.
83
93
84
94
### Get Cluster
85
95
@@ -108,10 +118,20 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses
108
118
"status": {
109
119
"conditions": [
110
120
{
111
-
"type": "Available",
121
+
"type": "Reconciled",
112
122
"status": "True",
113
-
"reason": "ResourceAvailable",
114
-
"message": "Cluster is accessible",
123
+
"reason": "ReconciledAll",
124
+
"message": "All required adapters reported Available=True or Finalized=True at the current generation",
125
+
"observed_generation": 1,
126
+
"created_time": "2025-01-01T00:00:00Z",
127
+
"last_updated_time": "2025-01-01T00:00:00Z",
128
+
"last_transition_time": "2025-01-01T00:00:00Z"
129
+
},
130
+
{
131
+
"type": "LastKnownReconciled",
132
+
"status": "True",
133
+
"reason": "AllAdaptersReconciled",
134
+
"message": "All required adapters report Available=True for the tracked generation",
115
135
"observed_generation": 1,
116
136
"created_time": "2025-01-01T00:00:00Z",
117
137
"last_updated_time": "2025-01-01T00:00:00Z",
@@ -304,7 +324,17 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses
304
324
"status": {
305
325
"conditions": [
306
326
{
307
-
"type": "Available",
327
+
"type": "Reconciled",
328
+
"status": "False",
329
+
"reason": "AwaitingAdapters",
330
+
"message": "Waiting for adapters to report status",
331
+
"observed_generation": 1,
332
+
"created_time": "2025-01-01T00:00:00Z",
333
+
"last_updated_time": "2025-01-01T00:00:00Z",
334
+
"last_transition_time": "2025-01-01T00:00:00Z"
335
+
},
336
+
{
337
+
"type": "LastKnownReconciled",
308
338
"status": "False",
309
339
"reason": "AwaitingAdapters",
310
340
"message": "Waiting for adapters to report status",
@@ -361,10 +391,17 @@ POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses
361
391
"status": {
362
392
"conditions": [
363
393
{
364
-
"type": "Available",
394
+
"type": "Reconciled",
395
+
"status": "True",
396
+
"reason": "ReconciledAll",
397
+
"message": "All required adapters reported Available=True or Finalized=True at the current generation",
398
+
"observed_generation": 1
399
+
},
400
+
{
401
+
"type": "LastKnownReconciled",
365
402
"status": "True",
366
-
"reason": "ResourceAvailable",
367
-
"message": "NodePool is accessible",
403
+
"reason": "AllAdaptersReconciled",
404
+
"message": "All required adapters report Available=True for the tracked generation",
368
405
"observed_generation": 1
369
406
},
370
407
{
@@ -446,8 +483,9 @@ See **[search.md](search.md)** for complete documentation.
446
483
The status object contains synthesized conditions computed from adapter reports:
447
484
448
485
-`conditions` - Array of resource conditions, including:
449
-
-**Available** - Whether resource is running at any known good configuration
450
-
-**Ready** - Whether all adapters have processed current spec generation
486
+
-**Reconciled** - Whether all adapters have reconciled at the current spec generation
487
+
-**LastKnownReconciled** - Whether resource is running at any known good configuration
488
+
-**Ready***(deprecated — use Reconciled)* - Whether all adapters have processed current spec generation
451
489
- Additional conditions from adapters (with `observed_generation`, timestamps)
452
490
453
491
### Condition Fields
@@ -464,7 +502,7 @@ The status object contains synthesized conditions computed from adapter reports:
464
502
- All above fields plus:
465
503
-`observed_generation` - Generation this condition reflects
466
504
-`created_time` - When condition was first created (API-managed)
467
-
-`last_updated_time` - When adapter last reported (API-managed, from AdapterStatus.last_report_time)
505
+
-`last_updated_time` - API-managed. For per-adapter conditions, taken from `AdapterStatus.last_report_time`. For aggregated conditions (`Reconciled`, `LastKnownReconciled`, `Ready`), computed as the oldest valid adapter report time within the relevant generation bucket — not the latest report time
468
506
-`last_transition_time` - When status last changed (API-managed)
Copy file name to clipboardExpand all lines: docs/search.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -98,14 +98,14 @@ Label keys must contain only lowercase letters (a-z), digits (0-9), and undersco
98
98
99
99
Query resources by status conditions: `status.conditions.<Type>='<Status>'`
100
100
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.
102
102
103
103
**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'`).
0 commit comments