Documentation
¶
Overview ¶
Package softdelete implements roadmap step 8: the soft-delete reconciler that honours the 30-day grace window from docs/organizations.md decisions O-8 (User delete) and O-13 (Org + Workspace delete).
One controller, three reconcilers:
User: watches User CRs; when status.deletionRequestedAt is set and the grace window has elapsed, drives the User cascade (personal Org → UMI → User CR).
Organization: watches Organization CRs; when status.deletionRequestedAt is set and the grace window has elapsed, drives the Org cascade (child Workspaces → in-Org Memberships → Org workspace → Org CR), stripping the org-scope UMI entries of every member as it goes.
Workspace: kcp Workspaces aren't in our scheme, so this branch polls (default every minute) — lists Org workspaces, lists each Org's child Workspaces, reads the annotation tenants.railgrid.ai/deletion-requested-at and, once the grace window expires, drives the Workspace cascade (kcp Workspace + workspace-scope UMI rows).
Inside the grace window the reconciler does NOT delete anything — it surfaces a DeletionInProgress condition on the parent CR and marks the corresponding UserMembershipIndex rows with softDeletedAt so the portal switcher (step 10) hides them. Undelete (the deletionRequestedAt / annotation being cleared back to nil) clears the marker and the condition; nothing brings back resources that the post-window cascade has already removed.
Index ¶
Constants ¶
const ( // DefaultGracePeriod is the soft-delete window per O-8 / O-13 when // the hub is not told otherwise. One number covers Users, Orgs and // Workspaces alike; the hub's --soft-delete-grace-period flag (the // chart's hub.softDeleteGracePeriod) overrides it for every kind at // once, and the REST undelete handlers read the same value so a // restore is refused exactly when the cascade would have started. // Matches the wording in docs/organizations.md. DefaultGracePeriod = 30 * 24 * time.Hour )
Variables ¶
This section is empty.
Functions ¶
func NewManager ¶
NewManager constructs a controller-runtime manager bound to the users workspace's rest.Config (typically the same one the organization bootstrap controller uses). Separated from the bootstrap controller's manager so soft-delete restarts don't take the bootstrap workqueue down.
func SetupWithManager ¶
func SetupWithManager(mgr manager.Manager, provisioner Provisioner, gracePeriod time.Duration) error
SetupWithManager registers the User and Organization watches with mgr and kicks off the Workspace-branch poll goroutine. The Workspace branch needs the manager's lifecycle (it stops when the manager's context cancels) which is why we run it as a Runnable instead of an independent goroutine in server.go.
gracePeriod is the recovery window; a non-positive value selects DefaultGracePeriod.
Types ¶
type Provisioner ¶
type Provisioner interface {
// DeleteOrgWorkspace removes the kcp Workspace at
// root:railgrid:orgs:{orgUUID}. Idempotent on NotFound.
DeleteOrgWorkspace(ctx context.Context, orgUUID string) error
// DeleteChildWorkspace removes the kcp Workspace at
// root:railgrid:orgs:{orgUUID}:{wsUUID}. Idempotent on NotFound.
DeleteChildWorkspace(ctx context.Context, orgUUID, wsUUID string) error
// ListChildWorkspaces returns the names of every child Workspace
// inside the Org workspace at root:railgrid:orgs:{orgUUID}. Empty if
// the parent Org workspace has been deleted.
ListChildWorkspaces(ctx context.Context, orgUUID string) ([]string, error)
// ListOrgWorkspaces returns the names (UUIDs) of every
// Organization workspace at root:railgrid:orgs. Drives the
// Workspace-branch poll sweep.
ListOrgWorkspaces(ctx context.Context) ([]string, error)
// GetWorkspaceDeletionRequestedAt reads the
// tenants.railgrid.ai/deletion-requested-at annotation from the
// child Workspace. The second return reports presence — callers
// can distinguish "no soft-delete requested" from "annotation
// present but malformed".
GetWorkspaceDeletionRequestedAt(ctx context.Context, orgUUID, wsUUID string) (*time.Time, bool, error)
// DeleteOrgMemberships removes every Membership CR inside the
// Organization workspace. Run during Org cascade so UMI strips
// see a clean delta.
DeleteOrgMemberships(ctx context.Context, orgUUID string) error
// ListOrgMemberships returns the user names (Membership.name) of
// every Membership in the Organization workspace. Used inside the
// grace window to mark every member's UMI rows, and during cascade
// to know which UMIs to strip.
ListOrgMemberships(ctx context.Context, orgUUID string) ([]string, error)
}
Provisioner is the slice of the kcp Bootstrapper that the soft-delete reconciler needs to inspect and tear down workspaces. Pulled out as an interface so unit tests use a fake without standing up embedded kcp; mirrors the WorkspaceProvisioner pattern in pkg/hub/controllers/organization.
Implemented by *pkg/hub/kcp.Bootstrapper.
type Reconciler ¶
type Reconciler struct {
// contains filtered or unexported fields
}
Reconciler is the shared receiver for the three soft-delete reconcile entry points. The User and Organization branches are watch-driven via controller-runtime; the Workspace branch is poll-driven via runWorkspacePoll(ctx).
func (*Reconciler) GracePeriod ¶ added in v0.2.31
func (r *Reconciler) GracePeriod() time.Duration
GracePeriod returns the configured recovery window, falling back to DefaultGracePeriod when the reconciler was built without one.