openova/products/catalyst
e3mrah 66e7768e8e
fix(helmwatch): emit Succeeded events for HRs Ready at attach time (#1510)
When catalyst-api restarts and the bridge re-attaches to an already-
converged child cluster, the informer initial-list returns HRs already
in Ready=True. The previous processEvent path relied implicitly on the
zero-value of w.states[componentID] (empty string) being different
from the derived state — which works today but would silently regress
if a future refactor pre-seeded w.states from a prior snapshot.

Caught on prov t112.omani.works (f2e7f02e6ffb6a18, 2026-05-15): 4 HRs
converged across primary + sin-2 regions before/after the pod restart
at 19:16, but the mothership Jobs API kept reporting:

    install-self-sovereign-cutover  → running   (kubectl: Ready=True)
    install-powerdns                → running   (kubectl: Ready=True)
    install-catalyst-platform       → running   (kubectl: Ready=True)
    install-sin-2:reloader          → failed    (kubectl: Ready=True)

D6 (0 pending / 0 running) and D7 (mothership ≡ child) both failed.

Fix shape: processEvent's emission policy is now EXPLICITLY "first
observation OR real transition". `hadPrev` (the two-return-value map
lookup) is false on the FIRST event for componentID regardless of the
state value, so the dispatch fires unconditionally on attach. The
dedupe via prev != state still suppresses sub-second status-patch
churn that helm-controller's observedGeneration touches produce.

Idempotency: the jobs.Bridge's lastState map dedupes (componentID,
state) re-emissions at the bridge layer (Bridge.OnHelmReleaseEvent
line ~478), and the openova-flow-server's TypeSnapshot envelope is
idempotent at the receiver — so a re-emit propagated by the
flow_emitter periodic loop is safe.

Two new tests pin the contract:
  - TestTransition_AttachTimeReady_EmitsSucceededViaSubscribe asserts
    a Watcher attaching to a child cluster with 4 already-Ready HRs
    emits exactly one State=installed event per HR, BOTH on the
    primary emit callback AND through Subscribe (the bridge wiring).
  - TestTransition_FirstObservation_NeverDedupsAcrossWatchers asserts
    that constructing a new Watcher against the same fake client
    (the Pod-restart shape) re-emits the full component-event set,
    because w.states is independent per Watcher.

Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 23:54:25 +04:00
..
bootstrap fix(helmwatch): emit Succeeded events for HRs Ready at attach time (#1510) 2026-05-15 23:54:25 +04:00
chart deploy: update catalyst images to 5f8ba85 2026-05-15 19:41:57 +00:00
README.md feat(consolidation): Phase 1 — move Catalyst-Zero apps + CI + manifests into public monorepo 2026-04-28 12:08:09 +02:00

OpenOva Catalyst (composite Blueprint)

The umbrella Blueprint bp-catalyst-platform — composes the Catalyst control plane.

Status: Deployed. Updated: 2026-04-28.

This product directory contains:

  • chart/ — the Helm chart that deploys Catalyst-Zero on a Kubernetes cluster (and every franchised Sovereign).
  • chart/templates/{ui,api}-deployment.yaml + service + ingress — the catalyst-ui (React SPA wizard scaffold) and catalyst-api (Go bootstrap API) workloads.
  • chart/templates/sme-services/ — 11 manifests for the legacy SME backend services + the consolidated console, admin, marketplace UI workloads (sourced from core/{console,admin,marketplace}/).
  • chart/templates/marketplace-api/ — manifests for the Go marketplace-api backend (sourced from core/marketplace-api/).
  • bootstrap/{ui,api}/ — the source code for catalyst-ui and catalyst-api (deployed via the catalyst-build CI workflow).

For the unified architecture and the wizard's target shape, see docs/PROVISIONING-PLAN.md, docs/ARCHITECTURE.md, and docs/SOVEREIGN-PROVISIONING.md.


How Catalyst-Zero is deployed today

A Flux Kustomization on the Catalyst-Zero cluster (Contabo k3s) reconciles products/catalyst/chart/templates/ from this public repo. CI workflows (.github/workflows/{catalyst,console,admin,marketplace,marketplace-api}-build.yaml) build and push images on every push to main, then the deploy step pins the image SHA into the corresponding manifest in this directory and commits back. Flux picks up the commit and rolls the deployment.

Image registry: ghcr.io/openova-io/openova/{catalyst-ui,catalyst-api,console,admin,marketplace,marketplace-api}:<sha>.

Migration status (per docs/PROVISIONING-PLAN.md)

Component Source location Image Status
catalyst-ui products/catalyst/bootstrap/ui/ ghcr.io/openova-io/openova/catalyst-ui public repo
catalyst-api products/catalyst/bootstrap/api/ ghcr.io/openova-io/openova/catalyst-api public repo
console core/console/ ghcr.io/openova-io/openova/console public repo (Phase 1)
admin core/admin/ ghcr.io/openova-io/openova/admin public repo (Phase 1)
marketplace core/marketplace/ ghcr.io/openova-io/openova/marketplace public repo (Phase 1)
marketplace-api core/marketplace-api/ ghcr.io/openova-io/openova/marketplace-api public repo (Phase 1)
sme-{auth,billing,catalog,domain,gateway,notification,provisioning,tenant} (still in openova-private/services/) ghcr.io/openova-io/openova-private/sme-* follow-up phase — source not yet moved