Skip to content

Control Center UI, sb-mock test backend, and optional upstream Ramen - #512

Open
schmidt-scaled wants to merge 12 commits into
mainfrom
feature/control-center-ui
Open

schmidt-scaled wants to merge 12 commits into
mainfrom
feature/control-center-ui

Conversation

@schmidt-scaled

Copy link
Copy Markdown
Contributor

Adds three optional, off-by-default capabilities to the operator Helm chart, and bumps the chart to 26.3.0.

1. Control Center web console (controlCenter.enabled)

The simplyblock web UI, deployed alongside the operator. Static document served by nginx with an in-pod reverse proxy in front of the three upstreams it uses (Kubernetes API, operator API, Prometheus). New monorepo component at control-center/; self-contained chart templates (control-center*.yaml, _control_center_helpers.tpl) with sbcc.* helpers. Serviceaccount/passthrough auth, optional Ingress and NetworkPolicy.

The console's own sources (index.html + .jsx) are not committed yet, so the image build (control_center_build/release) detects them missing and skips the image — the chart, mock, and Ramen are complete.

2. sb-mock — UI test backend (controlCenter.mock.enabled)

A stdlib-only Go server (control-center/mock/) that impersonates all three upstreams so the real, unmodified console runs against generated data: Kubernetes API (simplyblock + Ramen CRDs + core objects, incl. the proposed kinds with no CRD yet), operator API (fixture-driven), Prometheus (deterministic series). Writes persist and fire watch events but do nothing real; a simulator advances Ops/DRPC/migration phases. Seeded datasets (small-healthy, medium-degraded, large-scale, dr-failover, chaos, auto). Full test suite (go test).

3. Optional upstream Ramen (ramen.hub.enabled / ramen.drCluster.enabled)

RamenDR split across the two cluster roles: the hub operator on the management cluster (DRPolicy/DRPlacementControl/DRCluster via OCM) and the dr-cluster operator on managed clusters (VolumeReplicationGroups). Vendored verbatim from RamenDR/ramen v0.1.0-rc1 under ramen/; CRDs render as templates (ramen.installCRDs=false to skip where RHACM/ODF already ships them) and carry helm.sh/resource-policy: keep.

Validation

helm lint + helm template across all modes (default = zero optional objects; each feature renders correctly in isolation and combined); go vet/go test for sb-mock; helm package confirmed to bundle ramen/.

🤖 Generated with Claude Code

michixs and others added 12 commits September 9, 2026 23:19
Adds the simplyblock Control Center (the web console built with Claude
Design) as a monorepo component and wires it into the simplyblock-operator
Helm chart as an optional, off-by-default deployment.

- control-center/: image build (nginx + in-pod reverse proxy in front of
  the Kubernetes API, operator API and Prometheus), entrypoint, prepare
  script that strips fixtures and vendors React/Babel/fonts, and plain
  k8s manifests for non-Helm installs. The console sources (index.html
  and the .jsx files) land at control-center/ root in a follow-up.
- helm chart: control-center.yaml (SA/Deployment/Service/Ingress),
  control-center-rbac.yaml, an optional NetworkPolicy, and self-contained
  sbcc.* helpers whose upstream defaults resolve to this chart's static
  service names (simplyblock-operator:8080, simplyblock-prometheus:9090).
  Everything is gated on controlCenter.enabled=false in values.yaml.
- CI: control_center_build.yaml (branch images to DockerHub/ECR) and
  control_center_release.yaml (release images to quay.io), both skipping
  gracefully while the console sources are not committed; the release is
  chained into repo_release.yaml without blocking the chart publish.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A single Go binary (control-center/mock) that impersonates all three
upstreams the console proxies, so the real, unmodified UI runs against
generated data: the Kubernetes API (simplyblock CRDs, Ramen CRDs, core
objects), the operator HTTP API, and Prometheus.

- Kubernetes API engine over a generic in-memory store: discovery,
  list/get/create/update/delete, merge/JSON/apply patches, /status
  subresources, pod logs, label and field selectors, and streaming
  watches with resourceVersion/generation/uid bookkeeping. Writes
  persist and fire watch events but perform no underlying change.
- Seeded test data sets (small-healthy, medium-degraded, large-scale,
  dr-failover, chaos; --dataset auto picks randomly) rendered as a
  coherent object graph: devices reference their nodes, slots their
  PVCs, DRPCs their DRPolicy, and the Helm release Secret is a
  structurally real helm.sh/release.v1 payload. Same seed = same world.
- A simulator plays every reconciler: Ops objects walk Pending ->
  Running -> Succeeded/Failed (with subphases and a fail rate),
  spec.abort is honored, DRPC failovers and volume migrations progress,
  replication slots cut over, and transitions emit Events.
- Prometheus mock serves deterministic per-node series derived from the
  same topology; the operator API is fixture-driven and records
  unmatched requests (/mockctl/info) so the still-settling /v1 surface
  can be modeled incrementally.
- Admin API for e2e suites: /mockctl/reset (dataset/seed swap),
  /mockctl/advance (deterministic ticks with --sim-interval=0).
- --serve-ui serves the console statics directly for nginx-less local
  dev; in-cluster, controlCenter.mock.enabled=true deploys the mock
  next to the console and rewires its proxy env — the console image
  runs unmodified.
- CI: go vet/test plus a control-center-mock image on branch pushes,
  and a quay.io release image (not gated on the UI sources, which are
  still to be committed).

Stdlib-only, tested (store semantics, REST+watch roundtrips, selector
matrix, simulator state machines, per-dataset coherence and determinism).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploys RamenDR — the DR orchestrator the Control Center's DR views drive —
split across the two cluster roles the way Ramen itself is, both off by
default:

- ramen.hub.enabled  (management cluster): the hub operator reconciling
  DRPolicy, DRPlacementControl and DRCluster, driving placement through OCM.
- ramen.drCluster.enabled  (managed clusters): the dr-cluster operator
  reconciling VolumeReplicationGroups and executing the protection where the
  volumes live; enable it alongside the local simplyblock operator.

Manifests are vendored verbatim from RamenDR/ramen v0.1.0-rc1 (config/hub,
config/dr-cluster, config/crd/bases) under ramen/, and parameterized only for
namespace, image, resources and the RamenConfig (ramen.hub.config /
ramen.drCluster.config deep-merge over upstream defaults; ramen.s3StoreProfiles
shared). Object names are kept upstream (ramen-hub-operator,
ramen-dr-cluster-operator, namespace ramen-system) because Ramen refers to its
own names at runtime.

CRDs render as templates rather than from crds/, so they stay optional: only
the enabled halves install their CRDs, ramen.installCRDs=false skips them on
clusters where RHACM/ODF already ships Ramen, and every vendored CRD carries
helm.sh/resource-policy: keep so an uninstall never removes a CRD or the DR
state it holds. RBAC ClusterRoles are config/*/rbac/role.yaml verbatim.

Verified with helm lint and template across all modes (default = zero Ramen
objects; hub = 3 hub CRDs; dr-cluster = 6 execution CRDs; both = 9 with keep
policy; installCRDs=false = none) and that helm package bundles ramen/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New optional, off-by-default features since 26.2.x: the Control Center web
console (controlCenter.enabled), its sb-mock test backend
(controlCenter.mock.enabled), and upstream Ramen for disaster recovery
(ramen.hub.enabled / ramen.drCluster.enabled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- control-center/mock/main.go: "statics" -> "assets" in a comment.
- repo_codespell.yaml: ignore "notin" (the Kubernetes label-selector set
  operator used in control-center/mock, alongside the existing NotIn) and skip
  helm-charts/.../ramen, whose vendored-verbatim RamenDR CRDs spell "various"
  as "vaious" — not ours to correct, same rationale as the existing
  zz_generated skip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Populates control-center/ with the actual console from the design package
(index.html + the .jsx sources it loads, the mock-*.jsx fixture backend, the
CRD/RBAC design docs and screenshots). Until now only the deploy scaffold was
present, so the image build no-opped; with the sources committed,
control_center_build / control_center_release produce a real image.

- Console sources: index.html, config.js (dev preview; replaced at runtime),
  and all *.jsx. prepare.mjs strips the MOCK block and the mock-*.jsx fixture
  backend from the shipped image (verified: 24 assets, no mock-* shipped, no
  external subresource leaks).
- deploy/Dockerfile: fixed the prepare.mjs invocation to --out=/out. The
  script's arg reader only understands --flag=value, so the previous space
  form (--out /out) mis-parsed the path and would have failed the image build
  the moment sources existed.
- .dockerignore: keep mock/ (the Go test backend), screenshots/, uploads and
  docs out of the image build context.
- Docs: the package README.md becomes control-center/README.md (the product
  doc, so re-exports don't clobber a hand-written file); the prior monorepo
  integration guide moves to INTEGRATION.md, linked from the top of README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- deploy/build/prepare.mjs was silently untracked: the repo-root .gitignore
  ignores build/, so `git add` skipped it and the image build failed with
  "Cannot find module '/src/deploy/build/prepare.mjs'". Add a scoped
  control-center/deploy/.gitignore that re-includes build/prepare.mjs, and
  commit the file.
- codespell: skip the design-exported console sources (*.jsx, index.html,
  config.js, the CRD/RBAC design docs). They are re-exported from Claude
  Design, so their typos (asend, acount, ...) are corrected upstream, not
  here — same rationale as the vendored Ramen CRDs. Hand-maintained files
  (mock/, README, INTEGRATION.md) stay spell-checked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Syncs control-center/ to the new design export. The console no longer
transpiles JSX in the browser (30-40s cold start); the .jsx are compiled to
dist/app.js and dist/mock.js and index.html loads those.

- New: build.md, deploy/build/bundle.mjs (Babel compile step), dist/app.js,
  dist/mock.js. Refreshed all .jsx, index.html, prepare.mjs, the deploy
  scaffold (incl. CSP: unsafe-eval dropped now that Babel is gone) and design
  docs. Removed the screenshots/ set (dropped upstream).
- deploy/Dockerfile stage 2 now runs bundle.mjs (rebuilding dist/ in-image so
  it never ships a stale bundle) then prepare.mjs. Re-applied the --out=/out
  fix the export reverted (prepare.mjs only parses --flag=value).
- deploy/.gitignore now re-includes build/bundle.mjs too: the repo-root
  build/ ignore would otherwise silently drop it, as it did prepare.mjs.
- codespell: skip control-center/dist (generated bundles carry the same words
  as the already-skipped *.jsx).

Validated: prepare.mjs strips the fixture (dist/mock.js) and ships index.html
+ dist/app.js with no mock refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hree)

The prepare stage installed @babel/core, @babel/cli and @babel/preset-react
all at BABEL_VERSION=7.29.0, but only @babel/core published 7.29.0 —
@babel/cli and @babel/preset-react version on their own release trains and
have no 7.29.0, so npm failed with ETARGET and the image never built. 7.29.7
exists for all three (same "separate release trains" caveat the Dockerfile
already notes for the fontsource packages).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The multi-arch build ran the vendor and prepare stages under QEMU emulation
for linux/arm64 and crashed in npm/Babel ("qemu: uncaught target signal 4
(Illegal instruction)", exit 132). Both stages only produce arch-independent
output — vendored JS/fonts/svg and the Babel-compiled dist/ — so pin them to
--platform=$BUILDPLATFORM to run natively on the build host. Only the nginx
runtime stage stays per-target-arch, receiving the static assets by COPY.
amd64 already succeeded ("bundled 11 mock + 24 app sources into dist/").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adjusts the UI test backend to the CRD-redesign + multi-cluster RBAC design so
the console can be exercised against the intended model, even though almost
none of it exists on main. PARADIGM.md documents, principle by principle, what
is faithful and where a mock necessarily stands in.

Reproduced faithfully:
- Namespace layout + hierarchy labels: simplyblock-system, sb-mc-<managed>
  (transport), sb-sc-<cluster> (RBAC target), sb-dr-system, app namespaces —
  structure in labels (scope-kind, managed-cluster, storage-cluster), not names.
- Hub->agent projection: the StorageCluster exists user-facing in sb-sc-* and
  as a same-kind projection in sb-mc-* labelled managed-by:hub, whose status is
  agent-authored (lastSyncTime, agentConnected, Applied/AgentConnected
  conditions). The simulator plays the agent and heals drift.
- observedGeneration on every simplyblock kind, with the applied/pending
  distinction: a bumped hub spec reads Applied=Pending until the agent (a sim
  tick) catches observedGeneration up (design-crd-model §7.9).
- Hub-only conditions the managed cluster can't self-report (AgentConnected,
  InSync/DriftDetected) on ManagedCluster.
- Actor-stamp annotations on hub-authored objects (§3.2).
- RBAC vocabulary: eight aggregated sb:* ClusterRoles + rules-carrying children,
  RoleBindings/ClusterRoleBinding grants labelled managed-by:control-center,
  AccessGrant CR, and the cluster-scoped NodePoolAllocation envelope,
  StorageClusterClass and ManagedCluster.
- Authorization queries: SelfSubjectRulesReview / SubjectAccessReview and a
  scope-projection (/operator/v1/access/scopes, /access/self), driven by a
  configurable --viewer identity (global | none | <sb:role>@<ns>).

Documented deviations (see PARADIGM.md): no real identity/impersonation
(reviews are display-only, CRUD is not enforced); no server-side-apply field
managers; managed-by:hub is labelled but not admission-enforced; the
NodePoolAllocation CEL envelope, the agent CSR registration handshake, and the
RFC 8693 delegated-token exchange are out of the k8s/UI surface a mock sees.

New: tenancy.go, rbac.go, PARADIGM.md, paradigm_test.go. go vet + full test
suite pass; live-verified the layout, projection, drift heal, and authz.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adjusts the hub mock's RBAC to the product-facing model: namespaces are
tenants, and within a tenant there is one full-admin (CRUD) role plus one
read-only role per main object.

- Tenancy is now tenant-centric: sb-<tenant> namespaces (scope-kind=tenant)
  hold one or more whole clusters plus the tenant's DR-policies and
  DR-applications. Global admins create/manage them; extra tenants (large-scale,
  chaos) are generated empty with only a tenant-admin grant — ready to
  provision into. Replaces the per-cluster sb-sc-*/sb-sp-*/sb-dr-system layout.
- Role model (tenancy.go sbRoles): sb:infra-admin (global — manage tenants and
  bindings), sb:tenant-admin (full CRUD, the provisioning role), and one
  read-only role per main object: sb:cluster-reader, sb:dr-policy-reader,
  sb:dr-application-reader — each covering that object and its sub-objects.
  Adding a main object is one entry.
- Grants: a tenant-admin RoleBinding per tenant + the three reader bindings on
  the primary tenant, an infra-admin ClusterRoleBinding, and a tenant-scoped
  AccessGrant CR.
- DR-application is now a generated main object (protectedapplications +
  applicationfailovers) in the tenant namespace.
- authz (SSRR/SAR/scopes) unchanged in shape; --viewer accepts the new role
  names, and a scoped reader correctly denies cross-object and write access.
- PARADIGM.md updated (tenant layout, role table) with a deviation note: this
  simplifies the uploaded 8-role design per the product owner; the console
  .jsx/RBAC-DESIGN.md still describe the old model and must align on re-export.

go vet + full suite pass (new TestRoleModel); live-verified roles, per-tenant
grants, empty tenant, DR-application, and scoped-reader isolation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants