openova/products
e3mrah ee8d2e2b0e
fix(chroot): byte-identical /jobs + /cloud — kill fixture fallback, lazy-seed jobs.Store, single endpoint (#1054)
* fix(catalyst-api): rip out dangling sovereign_* route registrations + chart 1.4.56

PR #1050 deleted sovereign_more.go (which defined HandleSovereignUsers,
HandleSovereignCatalog, HandleSovereignSettings, HandleSovereignTopology)
but left four route registrations in cmd/api/main.go that still
referenced those handler methods. The catalyst-api build for the merged
revert (run 25439549879) failed with:

  cmd/api/main.go:690:39: h.HandleSovereignUsers undefined
  cmd/api/main.go:691:41: h.HandleSovereignCatalog undefined
  cmd/api/main.go:692:42: h.HandleSovereignSettings undefined
  cmd/api/main.go:693:42: h.HandleSovereignTopology undefined

That's why ghcr.io/openova-io/openova/catalyst-api:fdd3354 was never
published — only the UI image rolled. Result: omantel.biz catalyst-api
pod stuck in ImagePullBackOff.

Drop the four route registrations. Same baby, new address — the chroot
Sovereign uses the existing /api/v1/deployments/{depId}/* handlers via
the JWT-resolved deploymentId, not parallel-baby /api/v1/sovereign/*
endpoints.

Also revert two more parallel-baby fragments still on main:
  - getHierarchicalInfrastructure mode-aware fetcher → single mother
    URL (the chroot resolves deploymentId from the cookie and the
    mother-side topology handler serves byte-identical data once
    cutover-import has persisted the deployment record on the
    Sovereign's local store)
  - CatalogAdminPage.fetchApps mode-aware → /catalog/apps everywhere

Bump bp-catalyst-platform chart 1.4.55 → 1.4.56 and the cluster
Kustomization version pin to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sovereignDynamicClient): in-cluster fallback when running ON the Sovereign

The chroot Sovereign Console at console.<sov-fqdn> is the SAME catalyst-api
binary as the mother. When that binary runs ON the Sovereign cluster
(catalyst-system namespace on the Sovereign itself), there is no
posted-back kubeconfig — the catalyst-api IS in the cluster it needs
to talk to, and rest.InClusterConfig() returns the right credentials.

Without this, every endpoint that needs the Sovereign-side dynamic
client returned 503 with "sovereign cluster kubeconfig not yet posted
back" — including ListUserAccess (/users page), CreateUserAccess,
infrastructure CRUD, etc. Caught on omantel.biz 2026-05-06: /users
rendered "list user-access: HTTP 503" because the Sovereign-side
catalyst-api was looking for a kubeconfig that doesn't exist on the
chroot side of the cutover boundary.

Detection: SOVEREIGN_FQDN env (set on every Sovereign-side catalyst-api
deployment by the chart) matches dep.Request.SovereignFQDN. On the
mother, SOVEREIGN_FQDN is unset → unchanged behavior. On the chroot,
SOVEREIGN_FQDN matches the only deployment served (its own) → use
in-cluster.

Same fallback applied to tryDynamicClientLocked (loaderInputFor's
best-effort live-source client) so /infrastructure/topology and the
/cloud graph render with live data on the chroot too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(user-access): empty list when CRD absent + RBAC for chroot

Two coupled fixes for the /users page on chroot Sovereign Console:

1. catalyst-api-cutover-driver ClusterRole: grant read/write on
   useraccesses.access.openova.io. The Sovereign chroot's catalyst-api
   uses the in-cluster ServiceAccount (per PR #1052). The list call
   was returning 403 from the apiserver because the SA had no rule
   covering this CRD.

2. ListUserAccess: return 200 with empty items when the CRD itself
   is not installed (apierrors.IsNotFound). The access.openova.io
   CRD ships via a separate blueprint that may not yet be installed
   on a fresh Sovereign — the page should render its empty state,
   not a 500 toast.

Caught live on omantel.biz 2026-05-06 after PR #1052 unblocked the
in-cluster client path: list call surfaced first as 403 (RBAC), then
as 500 "server could not find the requested resource" (CRD absent).
Both now resolve to a 200 + [].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(chroot): byte-identical /jobs + /cloud — kill fixture fallback, lazy-seed jobs.Store from live cluster, single endpoint

Two parallel-baby paths still made the chroot diverge from the mother
on /cloud and /jobs/{jobId}. Both now ship one path that serves
byte-identical data on both surfaces.

1. CloudPage rendered fictional topology (Frankfurt, Helsinki,
   omantel-primary, omantel-secondary, edge-lb, vpc-net-eu, …) when
   the topology query errored — because it fell back to
   `infrastructureTopologyFixture` from `src/test/fixtures/`. That is
   a test-only file leaking into production via the production import
   tree, in direct violation of INVIOLABLE-PRINCIPLES #1 (no
   placeholder data — empty state when you don't know).

   Fix: drop the fixture fallback. On error → null → empty-state
   render. The mother shows the same empty state when its loader
   returns nothing; byte-identical.

2. JobsTable + JobDetail rendered a flat green-grid because the chroot
   was hitting `/api/v1/sovereign/jobs` which returns a minimal shape
   (no dependsOn, no parentId, no exec records). Mother's
   `/api/v1/deployments/{depId}/jobs` returns the rich shape from a
   per-deployment jobs.Store, which on the chroot starts empty (the
   mother's exportDeploymentToChild only ships the deployment record,
   not the jobs.Store contents).

   Fix: ship one URL on both surfaces — `/api/v1/deployments/{id}/jobs`.
   Add `chrootSeedJobsStoreIfEmpty` that runs at handler-time when
   SOVEREIGN_FQDN matches dep.Request.SovereignFQDN AND the per-
   deployment jobs.Store has 0 records: do a one-shot HelmRelease
   list via the in-cluster client (helmwatch.ListAndSnapshotHelmReleases
   — exported here, mirrors Watcher.SnapshotComponents without
   spinning up an informer), pass through snapshotsToSeeds +
   Bridge.SeedJobsFromInformerList. Subsequent calls read directly
   from the now-populated store and return rich Job records with
   dependsOn / parentId / status — exactly like the mother.

   useLiveJobsBackfill loses its mode-aware fetcher; the chroot UI
   uses the same `/api/v1/deployments/{id}/jobs` URL as the mother.

3. HandleDeploymentImport now also loads the imported record into the
   in-memory deployments map immediately, so `/deployments/{id}/*`
   handlers don't need a pod restart's restoreFromStore to see the
   chroot-imported deployment.

Bump bp-catalyst-platform 1.4.56 → 1.4.57 (chart + Kustomization).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 18:57:01 +04:00
..
axon feat(axon): make qwen3-coder thinking mode toggleable via request parameter 2026-04-26 09:20:33 +02:00
catalyst fix(chroot): byte-identical /jobs + /cloud — kill fixture fallback, lazy-seed jobs.Store, single endpoint (#1054) 2026-05-06 18:57:01 +04:00
cortex docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
fabric docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00
fingate docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
relay docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00