|
| 1 | +--- |
| 2 | +title: "Plan — OpenShift/CRC appendix" |
| 3 | +render_with_liquid: false |
| 4 | +--- |
| 5 | + |
| 6 | +# Plan — Appendix chapter "Running on OpenShift (CRC) locally" + runnable CRC assets |
| 7 | + |
| 8 | +Status: APPROVED by user 2026-10-07. Built via lgtm-relay (Opus plan → Sonnet build → Opus gate). |
| 9 | +Branch: `feat/openshift-crc-appendix`. |
| 10 | + |
| 11 | +## Goal |
| 12 | +Add an appendix chapter to the datamesh-reference-arch-python tutorial teaching how to run the |
| 13 | +existing capstone (today: minikube via helm) on a local **CRC / OpenShift Local** cluster, |
| 14 | +adapting to OpenShift idioms, and **LIVE-VERIFIED** on the local CRC instance. Modeled on the |
| 15 | +modernizing-enterprise-applications Part 9 deployment chapters (real runnable assets, applied |
| 16 | +live, documenting "dry-run passed / live apply broke" learnings). |
| 17 | + |
| 18 | +## Approach (REVISED 2026-10-07 — mirror the modernization OpenShift appendix) |
| 19 | +User direction: "use the modernization project as an example" / "we just did this with the |
| 20 | +modernization project." The modernization repo's PR #16 shipped a verified-on-CRC appendix: |
| 21 | +`_docs/33-appendix-openshift.md` + a dedicated **`openshift/helm/mea/`** chart (data-driven: |
| 22 | +ONE `app-deployment.yaml` ranging a `services:` map in values.yaml), **plain Postgres + Kafka |
| 23 | +StatefulSets (NO operators)**, a `mea-infra` ServiceAccount bound to the `nonroot-v2` SCC for the |
| 24 | +infra pods, app pods that **drop `runAsUser`** (restricted-v2 assigns a high UID), **Routes** |
| 25 | +(edge/Redirect TLS), images via the **internal registry**, plus `openshift/gitops/application.yaml` |
| 26 | +and `openshift/evidence/verification.txt`. |
| 27 | + |
| 28 | +**This SUPERSEDES the original "reuse umbrella chart + values-openshift.yaml + OLM operators" |
| 29 | +approach.** Instead: build a dedicated **`openshift/helm/datamesh/` chart** mirroring the |
| 30 | +modernization structure, adapted to the 7 Python/FastAPI services. Postgres + Kafka as plain |
| 31 | +StatefulSets (no CNPG/Strimzi/AMQ-Streams operator installs — avoids OLM complexity on CRC and |
| 32 | +matches what the user already did). Apicurio kept in the live core (contracts are central to the |
| 33 | +datamesh; light image). Rationale: the user explicitly pointed at this template; it is simpler, |
| 34 | +self-contained, and already proven on this exact CRC (2.64 / OCP 4.22.14 / 20GB / 8 vCPU). |
| 35 | + |
| 36 | +OpenShift delta vs the existing minikube helm path: (a) plain StatefulSets vs operator CRs for |
| 37 | +Postgres/Kafka; (b) Service NodePort→ClusterIP + Route objects; (c) image refs → internal |
| 38 | +registry (`image-registry.openshift-image-registry.svc:5000`); (d) drop `runAsUser` on apps + |
| 39 | +`nonroot-v2` infra SA; (e) config/endpoints templated off `{{ .Release.Namespace }}` (project |
| 40 | +`datamesh`); (f) anticipate the CRC-can't-reach-Docker-Hub snag → mirror postgres/kafka images |
| 41 | +through the host (a real learning from the modernization run). |
| 42 | + |
| 43 | +### Scope tiering (20GB CRC budget; bump to 24GB) |
| 44 | +- Core, live-verified: `datamesh` Project, CNPG Postgres, single-broker Kafka, Apicurio, the 7 |
| 45 | + services, a Route on graphql-gateway. Live check = GraphQL query through the Route hitting |
| 46 | + order→inventory→Postgres + an `order.placed` to Kafka. |
| 47 | +- Document-only (runnable, unverified): Istio/Service Mesh canary, KEDA/Custom Metrics |
| 48 | + Autoscaler, Kiali, LGTM observability. OpenMetadata OMITTED (heaviest, lowest value on CRC). |
| 49 | + |
| 50 | +### Rejected alternatives |
| 51 | +- New kustomize overlay — re-expresses chart logic that already renders; values-file reuse = less duplication. |
| 52 | +- All operators manual — OpenShift's point is OLM; use Subscriptions, manual only as fallback. |
| 53 | +- Istio+KEDA+OpenMetadata live — won't fit 20GB with Postgres+Kafka+7 svcs; document-only keeps honest. |
| 54 | +- In-cluster BuildConfig for images — 7 builds slow/fragile; push prebuilt to internal-registry route. |
| 55 | +- Project `capstone` — chose `datamesh` (matches deck vocabulary; override hardcoded `.capstone.svc`). |
| 56 | + |
| 57 | +## Steps |
| 58 | +0. Pull-secret gate (user) — RESOLVED: user confirms CRC + pull secret already set up on machine. |
| 59 | +1. Author runnable assets (Sonnet, no cluster) — mirror modernization `openshift/` tree: |
| 60 | + dedicated chart `examples/lgtm-datamesh/openshift/helm/datamesh/` (Chart.yaml, values.yaml with |
| 61 | + `services:` map for the 7 services, templates/: app-deployment.yaml [data-driven, drop runAsUser], |
| 62 | + app-service.yaml [ClusterIP], configmap-app.yaml, secret-app.yaml, postgres.yaml [StatefulSet], |
| 63 | + kafka.yaml [StatefulSet KRaft], apicurio.yaml, route.yaml [graphql-gateway + apicurio], |
| 64 | + serviceaccount-infra.yaml [+ nonroot-v2 SCC binding], _helpers.tpl, NOTES.txt); `openshift/README.md` |
| 65 | + (crc + build/push loop + image-mirror + helm install + verify commands); `openshift/gitops/application.yaml`; |
| 66 | + `openshift/build-and-push.sh`; `openshift/evidence/` placeholder. `helm template` renders clean; `bash -n` script. |
| 67 | +2. Draft chapter `_docs/11-running-on-openshift-crc.md` (order 12, ~3-5k words) + paired |
| 68 | + `assets/diagrams/18-*.svg`+`.excalidraw`; placeholders for live learnings. |
| 69 | +3. Live bring-up (serial, needs crc start): bump memory 24576, crc start, oc login, project, |
| 70 | + expose registry route, OLM Subscriptions, oc wait CSVs Succeeded, apply Postgres+Kafka CRs Ready. |
| 71 | +4. Images + app deploy + debug (serial): crc-build-push 7 images; helm install -f values-openshift; |
| 72 | + apply Routes; fix SCC/ImagePull/injection breakages; record each learning. |
| 73 | +5. Live verify + evidence (serial): all core pods Running; curl gateway Route 200 GraphQL; capture |
| 74 | + evidence into `examples/lgtm-datamesh/openshift/evidence/`. |
| 75 | +6. Fold evidence + learnings into chapter; add `_plans/decisions.md` DRA entries; jekyll build + |
| 76 | + check-liquid-collisions.sh + check-cross-references.sh. |
| 77 | + |
| 78 | +## Acceptance criteria |
| 79 | +- `_docs/11-running-on-openshift-crc.md` order 12, sorts after summary (order 11); jekyll build green; in nav. |
| 80 | +- Chapter ≥2000 words; ≥1 paired `assets/diagrams/18-*.svg`+`.excalidraw` (both parse). |
| 81 | +- Explicit dry-run-vs-live scope note + populated "what broke on live apply" section. |
| 82 | +- `helm template -f values-openshift.yaml` → ClusterIP + internal-registry refs, `oc apply --dry-run=client` clean. |
| 83 | +- `bash -n` clean on crc-up.sh / crc-build-push.sh / crc-teardown.sh. |
| 84 | +- Live: all core pods Running/Ready; operator CSVs Succeeded. |
| 85 | +- Live: `curl -k https://graphql-gateway-datamesh.apps-crc.testing/<path>` → 200 GraphQL across ≥2 products. |
| 86 | +- `openshift/evidence/` populated; `_plans/decisions.md` DRA entries; cross-ref + liquid checks pass. |
| 87 | + |
| 88 | +## Risks |
| 89 | +- Pull secret missing → crc start aborts (RESOLVED per user). |
| 90 | +- CRC OOM → pods Pending Insufficient memory → bump 24576, heavy tier doc-only, single-broker/1-instance. |
| 91 | +- SCC restricted-v2 denials (most likely live break) on apicurio/kafka-ui → scoped anyuid on specific SA only, document. |
| 92 | +- Image push auth/registry route → ImagePullBackOff/401 → defaultRoute patch + podman login $(oc whoami -t) --tls-verify=false. |
| 93 | +- Istio annotation-vs-label trap (silent, no sidecar) → use pod-template label form; mesh doc-only. |
| 94 | +- Hardcoded `.capstone.svc` endpoints leak → grep rendered template before apply. |
| 95 | +- CNPG/AMQ Streams CRD drift → oc apply schema rejection → verify apiVersion vs installed operator. |
| 96 | +- Operator not in catalogs → Subscription no CSV → verify redhat-operators + community-operators CatalogSources. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## PHASE 2 — Full platform tier (added 2026-10-07, user: "full set incl OpenMetadata", 32GB) |
| 101 | +APPROVED (OM best-effort). Opus plan returned; CRC resized to 32GB/14cpu/100GB (core green). |
| 102 | +Layers: KEDA (Custom Metrics Autoscaler + Kafka-lag ScaledObject on notification), Istio (OSSM3 |
| 103 | +Sail operator + IstioCNI, pod-label injection on 7 services, canary + STRICT mTLS, edge Route), |
| 104 | +observability (grafana/otel-lgtm all-in-one + re-enable OTLP env), Prefect (server+worker, reuse |
| 105 | +core Postgres), OpenMetadata (OpenSearch single-node + OM server, anyuid SA, best-effort). |
| 106 | +Assets: new examples/lgtm-datamesh/openshift/platform/ tree + install-platform.sh; app-chart |
| 107 | +mesh.enabled/observability.otlp.enabled flags (default off); chapter "full platform tier, live" |
| 108 | +section + flipped footer; DRA-008..014; per-layer evidence. Order: operators -> mesh CP -> |
| 109 | +observability -> meshed rollout (2/2) -> mTLS -> canary -> trace -> KEDA -> Prefect -> OM last. |
| 110 | + |
| 111 | +--- |
| 112 | + |
| 113 | +## BACKLOG — finish OpenMetadata next time in CRC (deferred 2026-10-07) |
| 114 | +OM is live except the server image, blocked by Docker Hub's unauthenticated pull-rate limit |
| 115 | +(`toomanyrequests`) — hit from BOTH the CRC VM and the host. Everything else is in place: |
| 116 | +OpenSearch is GREEN under the `openmetadata-opensearch` anyuid SA, the `openmetadata` DB/role + |
| 117 | +secrets are provisioned, and `om-app-values.yaml` already points `image.repository` at the internal |
| 118 | +registry mirror `image-registry.openshift-image-registry.svc:5000/datamesh/openmetadata-server:1.12.8`. |
| 119 | + |
| 120 | +To complete (next CRC session): |
| 121 | +1. `podman login docker.io` with a Docker Hub account (raises the pull-rate limit). A Bitwarden |
| 122 | + item may hold creds — `bw-get-secret` is available on this host. |
| 123 | +2. Mirror the image via the host into the internal registry: |
| 124 | + `REG=default-route-openshift-image-registry.apps-crc.testing`; |
| 125 | + `podman login -u kubeadmin -p $(oc whoami -t) --tls-verify=false $REG`; |
| 126 | + `podman pull docker.getcollate.io/openmetadata/server:1.12.8`; |
| 127 | + `podman tag ... $REG/datamesh/openmetadata-server:1.12.8`; |
| 128 | + `podman push --tls-verify=false $REG/datamesh/openmetadata-server:1.12.8`. |
| 129 | +3. `helm upgrade --install openmetadata open-metadata/openmetadata -n datamesh --version 1.12.8 -f om-app-values.yaml` |
| 130 | + (init container `run-db-migrations` runs the Postgres migrations, then the server starts). |
| 131 | +4. `oc apply -f openshift/platform/openmetadata/route.yaml`; wait `deployment/openmetadata` ready. |
| 132 | +5. `oc apply -f openshift/platform/openmetadata/ingestion-job.yaml`; confirm ≥1 catalog asset. |
| 133 | +6. Reconcile: fix the OpenSearch subchart's serviceAccount values key (the pod ran under `default` |
| 134 | + SA and had to be patched to `openmetadata-opensearch` live — find the correct `om-deps-values.yaml` |
| 135 | + key so it binds without a patch); then flip OM from "partial/best-effort" to "verified" in |
| 136 | + `_docs/11-running-on-openshift-crc.md` (footer + the OpenMetadata subsection) + add a DRA entry. |
0 commit comments