openova/.github
e3mrah dd1699afe3
feat(controllers): land useraccess-controller — fix silently broken Crossplane path (slice C5, #1095, P0) (#1128)
Per docs/EPICS-1-6-unified-design.md §3.5 and ADR-0001 §2.3 amendment,
K8s-to-K8s reconciliation belongs to thin in-cluster controllers, not
Crossplane Compositions. The existing useraccess.compose.openova.io
Composition writes RoleBindings via provider-kubernetes — but
provider-kubernetes is NOT installed on any production Sovereign
(caught in the EPIC-0 audit). Every UserAccess CR has been silently
no-op'd. This controller fixes that.

What lands:
- core/controllers/useraccess/cmd/main.go — controller-runtime Manager
  with leader election + signal handling, environment-only config
- internal/controller/{reconciler,desired,spec,status,types}.go — the
  reconciler. Watches UserAccess.access.openova.io/v1alpha1 (cluster-
  scoped, unstructured client) and owns RoleBinding +
  ClusterRoleBinding via Owns() so drift triggers reconcile via
  ownerRef indexing
- internal/labels/scope.go — Manara DNA scope matcher: AND-within /
  OR-across, wildcard scopes, EnforcedScopes() per catalog tier (the
  developer auto-injection of openova.io/env-type=dev)
- internal/controller/*_test.go + internal/labels/scope_test.go —
  26 unit tests with the controller-runtime fake client. Covers
  happy-path, multi-app/multi-ns fan-out, namespaces:["*"]→CRB,
  group subjects, drift detection+restore, orphan deletion on spec
  shrink, idempotency, invalid spec, ownerRef shape, NotFound no-op,
  and the 5-catalog-tier matrix
- deploy/{rbac,deployment}.yaml — ClusterRole/SA/Deployment with
  non-root, read-only-rootfs, drop-ALL caps, leader-election Role
- Containerfile — Alpine 3.20 final stage, CGO_ENABLED=0, UID 65534
- .github/workflows/useraccess-controller-build.yaml — event-driven
  build (push-on-main + PR test job), SHA-pinned image tags

Behaviour:
- Per UserAccess CR, materialises RoleBindings (per namespace) or
  ClusterRoleBindings (when namespaces:["*"]) referencing the
  canonical openova:application-{admin,editor,viewer} ClusterRoles
- ownerRef back to the UserAccess CR with controller=true +
  blockOwnerDeletion=true so K8s GC cascades deletes
- Drift detection: hand-mutated bindings are restored on next pass +
  Condition Drift=True surfaced for the UI
- Idempotent: steady-state reconcile = 0 K8s writes
- Status: phase (Pending|Active|Failed), rolebindingsCreated,
  observedGeneration, conditions[]

Out of scope per the brief:
- Crossplane Composition deletion (operator retires post-verify)
- 5-catalog-tier role inheritance (lands with EPIC-3 #1098)
- Keycloak realm-role sync (slice D1b, this controller is consumer)

Tests:
  go vet ./...                                # clean
  go test -count=1 -race ./...                # 26/26 pass
  go test ./internal/labels/... -run TestScope # full 5-tier matrix

Co-authored-by: Hatice Yildiz <hatiyildiz@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 00:04:07 +04:00
..
workflows feat(controllers): land useraccess-controller — fix silently broken Crossplane path (slice C5, #1095, P0) (#1128) 2026-05-09 00:04:07 +04:00
dependabot.yml chore(ci): add Dependabot for npm and GitHub Actions dependency updates 2026-03-19 13:42:02 +01:00