feat: gateway provisioning progress model and stepper UI - #269
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
| persisted in the API server and exposed via both the REST and gRPC APIs. | ||
|
|
||
| The conditions list SHALL only be present (non-null) while the gateway `phase` is | ||
| `Pending`, `Provisioning`, or `Failed`. When a gateway reaches `Running`, the |
There was a problem hiding this comment.
Let's always retain the list (even in a final success phase like 'Running') so that we can always see which phases the provisioning process went through
|
|
||
| --- | ||
|
|
||
| ### Requirement: GPP-03 -- UI Renders Provisioning Progress as a Stepper |
There was a problem hiding this comment.
Earlier in this spec we noted that 'UI layout, animation, and visual design are out of scope - the UI consumes the conditions and renders them according to its own design system' but this section seems to be describing visual design.
There was a problem hiding this comment.
Since the end-result of building this spec will in fact be something that's visible in the UI, let's remove that note about the scope from up top. Let's include UI elements and operation within this spec as well.
| - WHEN the user views the gateway detail | ||
| - THEN the UI SHALL display the stepper with `DatabaseReady` showing a failure | ||
| indicator | ||
| - AND the failure `message` SHALL be visible to the user |
There was a problem hiding this comment.
the message should be high level and not reveal low-level infrastructure details
bsquizz
left a comment
There was a problem hiding this comment.
Amber Review
Clean, well-reasoned spec. The provisioning step decomposition maps accurately to the actual reconciler flow and the PatternFly component mapping is correct. Six findings below - one Major (re-provisioning condition reset semantics) that I believe should be resolved before implementation, the rest are minor precision improvements.
| # | Severity | Finding | Line |
|---|---|---|---|
| 1 | Major | Re-provisioning condition reset semantics unspecified | 279 |
| 2 | Minor | Failed phase stops polling - clarify stepper terminal state |
155 |
| 3 | Minor | Condition status enum naming overlaps Gateway status field |
60 |
| 4 | Minor | Missing Degraded phase stepper behavior |
GPP-03 |
| 5 | Minor | Condition initialization ownership (API server vs control plane) | 98 |
| 6 | Nit | Amber persona domain model table references stale Fleet kind |
N/A |
Confidence: High (90%+)
Verdict: Approve with comments - resolve Finding 1 before implementation.
|
|
||
| - Per-step duration tracking or timing estimates ("ETA: 30 seconds") | ||
| - Retry controls in the UI (e.g., "Retry database provisioning") | ||
| - Provisioning progress for gateway updates (only initial provisioning) |
There was a problem hiding this comment.
Amber - Major: Re-provisioning condition reset semantics unspecified
This non-goal says "only initial provisioning," but the reconciler re-enters full provisioning when a Gateway is modified (image change via release, config update). When this happens, phase goes back to Provisioning (reconciler.go:1757). The spec does not say what happens to the provisioning_conditions list:
- Are conditions reset to
Pendingon re-provision? - Do they retain their previous
Completestate? - Does the UI show stale
Completecheckmarks during a re-provision that might fail at a different step?
This is the gap most likely to cause implementation ambiguity. I'd recommend either:
- Explicitly state that conditions are reset to
Pendingwhen the gateway re-entersProvisioning, or - Expand this non-goal to explain that conditions are frozen after initial provisioning and not updated on re-provision - but acknowledge the UX consequence (stepper shows stale state during updates).
Confidence: High
| - WHEN the GatewayReconciler completes the `DatabaseReady` step | ||
| - THEN it SHALL proceed directly to `GatewayDeployed` | ||
| - AND the `IdentityProviderReady` condition SHALL not be present | ||
|
|
There was a problem hiding this comment.
Amber - Minor: Failed phase stops polling - clarify stepper terminal state
GPP-02 says the gateway phase SHALL be set to Failed when a step fails. The phase vocabulary spec defines Failed as non-recoverable and stops polling (gateway-phase-vocabulary.spec.md lines 146-148). This means after a step failure the UI stops polling and the stepper is frozen - which is probably fine, but GPP-04 ("polling captures step transitions") only applies during Provisioning.
Consider adding a sentence to GPP-04 or GPP-02 clarifying that once Failed is set, polling stops and the stepper is in its terminal state until the user takes corrective action.
Confidence: High
| | Field | Type | Description | | ||
| |---|---|---| | ||
| | `type` | string | The condition identifier (e.g., `EnvironmentReady`) | | ||
| | `status` | enum | `Pending`, `InProgress`, `Complete`, `Failed` | |
There was a problem hiding this comment.
Amber - Minor: Condition status enum naming overlap
The condition status field uses the enum Pending, InProgress, Complete, Failed. The Gateway resource already has a top-level status field (proto field 11) with different semantics. While the spec is clear these are condition-level statuses, implementers will need to be careful about naming in the protobuf and OpenAPI schemas.
A brief note acknowledging this distinction and suggesting an implementation naming convention (e.g., a nested ProvisioningConditionStatus message type, or a condition_status field name) would reduce ambiguity.
Confidence: High
| - WHEN the user views the gateway detail | ||
| - THEN the failed step SHALL render as `ProgressStep` with `variant="danger"` | ||
| - AND the `description` prop SHALL display the failure message | ||
| - AND subsequent steps SHALL render as `variant="default"` (inactive) |
There was a problem hiding this comment.
Amber - Minor: Missing Degraded phase stepper behavior
GPP-03 covers Provisioning, Running, and Failed phases but doesn't specify stepper behavior during Degraded. The reconciler transitions a gateway to Degraded if the deployment readiness window times out (reconciler.go:1783) - at that point all manifests are applied but the workload is not ready.
Should GatewayHealthy show as Failed? InProgress? Adding a brief scenario for the Degraded case would close this gap.
Confidence: High
|
|
||
| #### Scenario: Conditions appear on a newly created gateway | ||
|
|
||
| - GIVEN a user creates a new Gateway resource |
There was a problem hiding this comment.
Amber - Minor: Condition initialization ownership could be clearer
This scenario says conditions are initialized "WHEN the API server persists the Gateway." But the control plane is the component that knows which steps apply (e.g., whether OIDC is configured). If the API server initializes conditions, it needs to inspect the gateway config to decide whether to include IdentityProviderReady - duplicating domain logic that currently lives in the reconciler.
Consider clarifying whether initialization happens:
- In the API server at creation time (simpler for clients, but API server needs domain awareness), or
- In the control plane on first reconciliation (aligns with current architecture where the reconciler owns provisioning logic).
Confidence: High
bsquizz
left a comment
There was a problem hiding this comment.
Amber Review (follow-up)
All five findings from the previous review are addressed:
| # | Original Finding | Resolution |
|---|---|---|
| 1 | Major: Re-provisioning condition reset semantics | Resolved. New scenario "Conditions reset on re-provisioning" explicitly states conditions reset to Pending. Non-goal reworded from "only initial provisioning" to "partial re-provisioning" - more precise. |
| 2 | Minor: Failed phase stops polling |
Resolved. New scenario "Failed phase is a terminal stepper state" + "Polling stops on Failed phase" added. GPP-04 now has an explicit paragraph aligning with phase vocabulary polling rules. |
| 3 | Minor: Condition status naming overlap |
Resolved. Field renamed to condition_status with naming note explaining the protobuf enum ProvisioningConditionStatus. |
| 4 | Minor: Missing Degraded phase behavior |
Resolved. New scenario "Degraded gateway shows health step with warning" + new row in the PatternFly mapping table using variant="warning". |
| 5 | Minor: Condition initialization ownership | Resolved. New "Condition Initialization Ownership" section explicitly assigns ownership to the control plane with clear rationale. Scenario updated to "Control plane initializes conditions on first reconciliation". |
One observation (not a blocker): The Degraded variant mapping means the UI rendering is no longer a pure function of condition_status alone - it needs to check both condition_status and phase to distinguish danger from warning on GatewayHealthy. The spec is clear about this, but implementers should be aware of the branching logic. A helper like stepVariant(condition, phase) would keep this contained.
Verdict: Approved. Clean spec, ready for implementation.
| | `InProgress` | `pending` | `true` | Step shows a spinner animation indicating work in progress | | ||
| | `Complete` | `success` | `false` | Step shows a green check mark | | ||
| | `Failed` | `danger` | `false` | Step shows a red X icon | | ||
| | `Failed` (on `GatewayHealthy` when `phase` is `Degraded`) | `warning` | `false` | Step shows a warning icon (recoverable, polling continues) | |
There was a problem hiding this comment.
Amber - Observation (not a blocker)
The warning variant for GatewayHealthy when phase is Degraded means the component mapping is no longer a pure function of condition_status - the UI also needs to inspect the gateway's phase. This is the right design (recoverable vs non-recoverable failure distinction is valuable), but implementers should encapsulate this in a small helper like stepVariant(condition, phase) rather than spreading the branching across the template.
Confidence: High
|
Amber: Last observation addressed in 3cc3132 - |
|
|
||
| #### PatternFly Component Mapping | ||
|
|
||
| The UI SHALL use PatternFly's `ProgressStepper` with `isVertical` layout. Each |
There was a problem hiding this comment.
Let's NOT use 'isVertical' layout
8cadb56 to
a37429e
Compare
Amber reviewStatus: Complete |
Amber reviewStatus: Stopped The pull request head changed before Amber posted the review. A later job can review the new head. |
Amber reviewStatus: Stopped The pull request head changed before Amber posted the review. A later job can review the new head. |
Amber reviewStatus: Complete |
Amber reviewStatus: Stopped The pull request head changed before Amber posted the review. A later job can review the new head. |
Amber reviewStatus: Stopped The pull request head changed before Amber posted the review. A later job can review the new head. |
Amber reviewStatus: Complete |
Amber reviewStatus: Complete |
Add provisioning_conditions field to the Gateway resource, enabling sub-phase progress tracking during gateway provisioning. The field is a JSONB array of condition objects (type, condition_status, message) that the control plane updates at each reconciliation step boundary. Proto: ProvisioningConditionStatus enum + ProvisioningCondition message API server: GORM model field, JSONB migration, OpenAPI readOnly array, gRPC/REST presenters, gRPC UpdateGateway handler Control plane: ProgressReporter callback, InitConditions/SetCondition helpers, step-boundary reporting in ReconcileGateway and Handle SDK: regenerated Go and TypeScript clients UI: PatternFly ProgressStepper on gateway detail page with stepVariant helper mapping condition status + phase to step variants Web console: parseProvisioningConditions adapter for SDK JSON string Tests: 8 API server integration tests (gRPC round-trip, REST readOnly, condition progression, failure messages, IdP step, preservation on unrelated updates), UI component tests (8 cases), e2e provisioning conditions validation in the Kind cluster test suite Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up the provisioning progress stepper to display real-time gateway provisioning status. Fix the SDK response parsing to handle structured JSON arrays (not just strings), show the stepper immediately after creation with default pending steps, add console readiness and provisioned completion steps, and display a waiting state in the connection tab during provisioning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set min-height on step titles to reserve space for 2-line labels, preventing layout shift when longer active labels like "Verifying gateway health" transition to shorter completed labels like "Health verified". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adapt provisioning stepper UI to main's renamed CommandBlock component, sort message keys alphabetically, add missing Content import and manageServiceAccounts message, update tests to match new provisioning behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nters Address valid Amber review findings: unmarshal errors for provisioning_conditions were silently swallowed in both the REST and gRPC presenters. Add glog.Warningf on the error path so corrupt JSON is surfaced in server logs. Also log a warning in conditionStatusFromProto when an unknown enum value falls through to the default case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix staticcheck QF1008 lint error (gateway.Meta.ID -> gateway.ID), regenerate Go and TypeScript SDKs to sync spec SHA256 hashes after rebase, and add missing provisioning_conditions field to the web console gateway test fixture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The e2e test queries the provisioning message by role="status", but the div wrapper was missing the ARIA role attribute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Client-Derived Presentation Steps section documenting that the UI synthesizes ConsoleReady and Provisioned steps beyond the 5 server-side conditions. These are owned by the UI, never persisted, and never sent over the wire. Also correct the PatternFly Component Mapping table to use the info variant for InProgress (not pending), matching the implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The e2e test was querying by role="status" with the old connectionLoginUnavailable message, but the provisioning state now renders the connectionWaitingForProvisioning message instead. Use getByText to match the visible "Waiting for gateway provisioning..." text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Failed and Degraded gateways were treated as still provisioning because isProvisioning checked phase !== "running". This hid connection commands behind an infinite "Waiting for gateway provisioning..." spinner. Now only empty, Pending, and Provisioning phases trigger the provisioning state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6bbc440 to
40d51a4
Compare
Amber reviewStatus: Complete |
|
/retest |
1 similar comment
|
/retest |
|
/retest |
2 similar comments
|
/retest |
|
/retest |
Signed-off-by: Brandon Squizzato <35474886+bsquizz@users.noreply.github.com>
amber-review-bot
left a comment
There was a problem hiding this comment.
Verdict
This PR threads a provisioning-progress model from the control plane through the API server (proto, gRPC, REST, SDK, migration) into a PatternFly 6 stepper, with unit, component, and e2e coverage. At this head the previously flagged reconcile-ordering change is now confirmed safe, so there are no blockers or security issues; the remaining items are accepted minor notes plus cross-PR coordination that needs a maintainer decision.
What I checked
- Error handling, secret hygiene, and log-injection safety across the new Go paths (control-plane reporter, gRPC handler/presenter, REST presenter).
- The new proto enum/message, migration, and whole-row
UpdateGatewaypartial-update semantics. - Reconcile ordering and the condition state machine in
gateway/reconciler.goandreconciler/reconciler.go. - The stepper variant mapping and provisioning-visibility gating in the UI.
- Test Diff Scrutiny on modified assertions in pre-existing tests.
- Follow-up discussion on prior Amber findings, re-checked against the current head. The new delta since my last review is the author's replies confirming the reconcile-ordering rationale, which I verified in code.
Findings
All remaining findings already have Amber inline threads at this head; I have not duplicated them.
Minor
-
Write amplification on progress reporting (
components/control-plane/internal/reconciler/reconciler.go).updateProvisioningConditionsissues a full gRPCUpdateGateway(server-side get + whole-rowReplace) at every step boundary - roughly a dozen round-trips per successful reconcile plus the phase/health writes, each emitting a fresh watch event. Correct (the partial-update handler avoids clobbering other columns), but consider coalescing at fleet scale. Author accepted as a future optimization. Existing thread: #269 (comment) Confidence: Medium. -
Ignored marshal error and set-only semantics in the handler (
components/api-server/plugins/gateways/grpc_handler.go).data, _ := json.Marshal(conditions)drops the error; it mirrors the existingserver_dns_namespattern and cannot realistically fail for this fixed struct (author confirmed), so this is not a regression. Also noteprovisioning_conditionsis only written whenlen(req.ProvisioningConditions) > 0, so the field can never be reset to empty throughUpdateGateway- intentional since the control plane always sends the full list, worth keeping in mind. Existing thread: #269 (comment) Confidence: High.
Test Diff Scrutiny
The modified assertion in packages/gateway-management-ui/src/gateways/gateway-data.test.ts ("Unknown" -> "Provisioning") flips a display fallback, not a validation or optional-to-required contract. It is paired with the gateway-data.ts source change and is an intentional product decision (show "Provisioning" the moment Create is pressed rather than a confusing "Unknown"). This is an additive presentation change, not a silently removed guarantee.
Cross-PR coordination
-
#276 (safe gateway release rollout): Direct structural conflict. Both PRs assign the same proto field number 21 in
UpdateGatewayRequest(provisioning_conditionshere vsobserved_release_idthere), and both edit the whole-rowUpdateGatewaypartial-update handler ingrpc_handler.go, thegatewaysmigration, and the regeneratedgateways.pb.go/SDKs. Maintainers must set a merge order; whichever lands second must renumber itsUpdateGatewayRequestfield and regenerate. Beyond the wire format, the two encode competing readiness models for the same Provisioning->Running/Degraded transition: this PR derivesGatewayHealthyfrom the existing 2-minuteWaitForGatewayReady(reconciler/reconciler.go), while #276 judges readiness against the newly rolled-out release via a newreconciler/health.go. The authoritative health model needs agreement so the reported condition matches actual health. -
#151 (gate re-provisioning on desired-state convergence): This PR initializes and re-sends conditions (
InitConditions+ReportProgress) only after the phase-based gate inGatewayReconciler.Handle(below the early return forRunning/Provisioning/Degraded), so the spec's "conditions reset on re-provisioning" scenario is unreachable until that gate changes. #151 re-keys exactly that gate from phase-based to convergence-based (observed_generation) and also touches the Gateway/UpdateGatewayRequestproto, theUpdateGatewayhandler, the migration set, and the SDK. The reconcile-gate semantics (and proto numbering) must be coordinated so condition re-initialization on drift/upgrade actually runs. This matches the author's own note that the fix is a reconcile-gate decision coupled to #151. -
#261 (intermittent "Degraded" status fix): Reworks the Provisioning->Degraded timing, introducing a configurable
deploymentReadyTimeout(default 10 minutes) inreconciler/health.gobefore marking Degraded. This PR'sGatewayHealthycondition is instead driven by the inline 2-minuteWaitForGatewayReadypath inreconciler/reconciler.go, which reports a "Gateway health check timed out" failure and sets Degraded at that window. The two encode competing timeout/degraded semantics for the same transition; a decision on the authoritative timing model is needed so the reported condition and the health phase stay consistent.
Previous concerns
- Major - Keycloak reorder: addressed/retracted. I verified
reconcileKeycloakClient(components/control-plane/internal/gateway/reconciler.go:1338) only calls the Keycloak admin API viakeycloak.NewClient(...)and setsnsConfig.Gateway.OIDC; it reads no cert-manager output (no trust bundle, no TLS material), so moving Step 3 ahead of the cert-manager block introduces no reverse dependency. The only residual effect (idempotently reconciling the Keycloak client before a missing-cert-manager reconcile fails) is harmless. Retracted in the thread. - Minor - write amplification: still present (by design).
updateProvisioningConditionsinreconciler.gois still called at each step boundary; author deferred as a future optimization. Carried forward as Finding 1. - Minor - ignored marshal error / set-only update: still present (accepted).
grpc_handler.gostill usesdata, _ := json.Marshal(...)inside thelen(...) > 0guard; not a regression. Carried forward as Finding 2. - Major - isProvisioning treats Failed/Degraded as provisioning: addressed.
gateway-pages.tsxnow derivesisProvisioningfrom!phase || phase === "pending" || phase === "provisioning", so Failed/Degraded no longer render the indefinite spinner. - Minor - silent unmarshal drop in REST/gRPC presenters: addressed.
presenter.goandgrpc_presenter.gonow log the unmarshal error viaglog.Warningfinstead of silently dropping the list. - Minor - default UNSPECIFIED coerced to Pending: addressed.
conditionStatusFromProtonowglog.Warningfs on an unknown enum before returning the fallback. - Major - InProgress mapped to info not pending: addressed/retracted. The PatternFly demo maps the current step to
info; commit 7031042 updated the spec Component Mapping table so spec andstepVariantagree. - Major - ConsoleReady/Provisioned steps undocumented: addressed. Commit 7031042 added the "Client-Derived Presentation Steps" section documenting the two UI-synthesized steps and their ownership.
- Minor - getByRole -> getByText: cannot verify as a regression. PatternFly's
Spinnerinjects hidden "Loading..." text so the accessible-name query cannot match;role="status"is still on the element and covered by the skeleton test. Accepted; the ARIA role for this specific message is no longer directly asserted. - Minor - re-provisioning reset reachability / per-gateway OIDC gating: cannot verify as fixed; deferred to cross-PR coordination. Reset still runs only after the phase gate and
IdentityProviderReadyis still gated on the platform-widekeycloakConfig. See the Cross-PR coordination note on #151.
Findings Summary (ordered by severity, highest first)
- [Minor] Progress reporting issues a whole-row
UpdateGatewayper step (write amplification) - Performance (reconciler.go) - [Minor] Ignored marshal error and set-only
provisioning_conditionsupdate - Robustness (grpc_handler.go)
Convention Checklist
| Convention | Result |
|---|---|
No panic() in production code |
Pass |
Errors wrapped with fmt.Errorf context |
Pass |
errors.IsNotFound/404 handling |
Pass |
| No secrets in logs or responses | Pass |
| No silent partial failures (unmarshal now logged) | Pass |
| Input validated | Pass |
| Reconcile pattern (not create-or-skip) | Pass |
| SecurityContext on pod specs | N/A (no new pod specs) |
| Image references consistent | N/A |
| OpenAPI client not hand-edited (generated) | Pass |
| DB migration idempotent + reversible | Pass |
| Test Diff Scrutiny on modified assertions | Pass |
| No em dashes | Pass |


Summary
specs/platform/gateway-provisioning-progress.spec.mddefining a provisioning progress model with 5 server-side conditions (EnvironmentReady,DatabaseReady,IdentityProviderReady,GatewayDeployed,GatewayHealthy) and 2 client-derived presentation steps (ConsoleReady,Provisioned)provisioning_conditionsandgateway_versionfields on the Gateway resource (proto field 23/24, REST + gRPC, Go/TypeScript SDK)ReportProgresscallback during reconciliationProgressSteppercomponent on the gateway detail page showing provisioning progress across all gateway phasesTest plan
🤖 Generated with Claude Code