openova/products
e3mrah 501b15339a
feat(catalyst-chart): land Organization CRD orgs.openova.io/v1 (slice B1, #1095) (#1106)
Realizes the Organization CRD spec from docs/EPICS-1-6-unified-design.md §3.2.1.
Per ADR-0001 §2.7 a tenant is namespace + vCluster + Keycloak group; this CRD
is the K8s-native parent of those three artifacts plus billing/identity
attributes. Customer (real billing) and internal (chargeback/showback) Orgs
share the SAME shape and SAME code path — billingMode is the only dimension
that differs.

Cluster-scoped resource (Organizations span vClusters and host clusters; not
namespace-bound).

Spec carries:
- slug — pattern-validated lowercase 3-32 chars; `not.enum` rejects reserved
  names (system, flux, crossplane, catalyst, gitea, hetzner, etc., per
  NAMING-CONVENTION.md §2.5)
- displayName — minLength=1
- kind — enum customer | internal
- tier — enum sme | corporate
- billingMode — enum real | chargeback | showback
- sovereignRef — FQDN pattern
- parentOrg — optional, for nested orgs in corporate Sovereigns
- defaultEnvironmentType — enum prod|stg|uat|dev|poc, default prod
- owners[] — minItems=1, role enum owner|admin|developer|viewer
- identity — federationProvider enum (azure-sso|okta|generic-oidc) +
  clientSecretRef (SealedSecret name+key — plaintext NEVER on the CR)

Status carries vcluster.{name,hostCluster,phase}, keycloakGroup.{id,path,realm},
giteaOrg.{name,repos[]}, conditions[], observedGeneration.

additionalPrinterColumns surface slug, kind, tier, billing, sovereign, vcluster
phase, age via `kubectl get org`.

Validated against a real k3s control plane:
- 2 valid samples accepted (corporate Org with Azure-SSO + internal Org with
  parentOrg/chargeback)
- 2 invalid samples REJECTED with all 12 seeded error vectors:
  * slug=system → not.enum reserved-name rejection
  * slug=AC → pattern + length rejection
  * displayName="" → minLength=1
  * displayName missing → required
  * kind=vendor → enum
  * tier=premium → enum
  * billingMode=invoice → enum
  * sovereignRef="not a domain" → FQDN pattern
  * sovereignRef missing → required
  * defaultEnvironmentType=production → enum
  * owners=[] → minItems=1
  * identity.federationProvider=saml → enum

Refs: #1094, #1095, docs/EPICS-1-6-unified-design.md §3.2.1, NAMING-CONVENTION.md §1.5/§2.5/§4.6, ADR-0001 §2.7

Co-authored-by: hatiyildiz <hatiyildiz@noreply.openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:00:19 +04:00
..
axon feat(axon): make qwen3-coder thinking mode toggleable via request parameter 2026-04-26 09:20:33 +02:00
catalyst feat(catalyst-chart): land Organization CRD orgs.openova.io/v1 (slice B1, #1095) (#1106) 2026-05-08 22:00:19 +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