softdelete

package
v0.2.33 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

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

View Source
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

func NewManager(cfg *rest.Config, scheme *runtime.Scheme) (manager.Manager, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL