softdelete

package
v0.2.27 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 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 (
	// GracePeriod is the soft-delete window per O-8 / O-13. Hardcoded
	// (not flag-tunable) so the contract surfaced to users is a single
	// number; matches the wording in docs/organizations.md.
	GracePeriod = 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) 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.

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).

Jump to

Keyboard shortcuts

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