Documentation
¶
Overview ¶
Package membershipindex implements the UMI invariant reconciler: a controller that watches UserMembershipIndex CRs in root:railgrid:users and repairs rows that strand a user.
The invariant: every workspace-scope row implies an org-scope row for the same Organization. The portal's org switcher is built exclusively from org-scope rows (restapi.listOrgs), and every workspace lives inside an org — so a UMI holding a workspace-scope row with no matching org-scope row describes a workspace the user has been granted but can never reach. They see nothing, while every individual object (RBAC binding, UMI row, Membership CR) looks correct in isolation.
The REST layer keeps writing both rows on the happy path (restapi.addWorkspaceMembership cascades the org membership), but the index is maintained by dual writes across failure domains — a handler crash between writes, a partial rollout, or an older hub can all leave the index inconsistent. Following the model in platform-mesh's tenancy-operator (RFC 010), consistency of derived state is a controller's job, not a property every writer must individually guarantee: handlers record intent, this reconciler converges the index, and drift heals on the next resync instead of persisting until someone files a bug.
What it deliberately does NOT do:
- Invent workspace rows from Membership CRs (the workspace-scope source of truth IS the UMI; there is nothing to reconcile against).
- Resurrect rows for Organizations that no longer exist or are soft-deleted — teardown is the softdelete reconciler's domain, and healing must never race it back to life.
- Touch roles on existing rows (the REST layer owns role changes).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewManager ¶
NewManager constructs a controller-runtime manager bound to the root:railgrid:users workspace config (Bootstrapper.UsersConfig), matching the organization and softdelete managers. Separate manager so an invariant-repair crash can't take their workqueues down.
func SetupWithManager ¶
func SetupWithManager(mgr manager.Manager, ensurer MembershipEnsurer) error
SetupWithManager registers the UserMembershipIndex watch with mgr.
Types ¶
type MembershipEnsurer ¶
type MembershipEnsurer interface {
// EnsureOrgMembership creates an org-scope Membership CR inside the
// Organization workspace granting userName the given role. Idempotent.
EnsureOrgMembership(ctx context.Context, orgUUID, userName, role string) error
}
MembershipEnsurer is the one Bootstrapper capability this controller needs: recording the org-scope Membership CR that mirrors the healed index row. Narrow so tests fake it without an embedded kcp.
Implemented by *pkg/hub/kcp.Bootstrapper.
type Reconciler ¶
type Reconciler struct {
// contains filtered or unexported fields
}
Reconciler enforces the ws-row→org-row invariant on one UserMembershipIndex per reconcile.