Documentation
¶
Overview ¶
Package sandboxalloc decides and records how sessions are placed on remote sandboxes. It is the only writer of session_sandbox_assignments and, once wired, of the session's sandbox pin.
Index ¶
- Constants
- Variables
- func FromEntity(e *types.SessionSandboxAssignment, machine *types.SandboxUserMachine) allocation.Assignment
- func ToEntity(a allocation.Assignment, digest string) (*types.SessionSandboxAssignment, *types.SandboxUserMachine)
- type ConfigReader
- type GCReport
- type PlaceRequest
- type Service
- func (s *Service) Digest(m allocation.UserMachine) string
- func (s *Service) ForkAssignment(source allocation.Assignment, targetSessionID string) allocation.Assignment
- func (s *Service) Lookup(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)
- func (s *Service) Namespace() string
- func (s *Service) Now() time.Time
- func (s *Service) Place(ctx context.Context, req PlaceRequest) (allocation.Assignment, error)
- func (s *Service) Release(ctx context.Context, sessionID string) (interfaces.RetiredSession, error)
- func (s *Service) Repo() interfaces.SandboxAllocationRepository
- func (s *Service) Retire(ctx context.Context, sessionIDs []string) ([]interfaces.RetiredSession, error)
- func (s *Service) SessionAllocation(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)
- func (s *Service) UserMachinesByConfig(ctx context.Context, configTenantID uint64, configID string) ([]allocation.UserMachine, error)
- func (s *Service) VerifySessionAllocation(ctx context.Context, sessionTenantID uint64, sessionID string, ...) error
- type SessionReader
- type UserMachineGC
- type UserMachineOps
- type WorkspaceCleaner
- func (c *WorkspaceCleaner) DeleteSessions(ctx context.Context, tenantID uint64, idsByOwner map[string][]string) (map[string]bool, int, error)
- func (c *WorkspaceCleaner) OwnerMachines(ctx context.Context, ownerTenantID uint64, ownerID string) ([]allocation.UserMachine, error)
- func (c *WorkspaceCleaner) ReclaimOwner(ctx context.Context, ownerID string)
- func (c *WorkspaceCleaner) ReclaimOwnerMachines(ctx context.Context, machines []allocation.UserMachine)
- func (c *WorkspaceCleaner) ReleaseMachine(ctx context.Context, m allocation.UserMachine) (bool, error)
- func (c *WorkspaceCleaner) ReleaseOwnerAssignments(ctx context.Context, ownerID string) error
- func (c *WorkspaceCleaner) ReleaseSessions(ctx context.Context, sessionIDs []string) (map[string]bool, error)
- func (c *WorkspaceCleaner) RetryDue(ctx context.Context, limit int) (int, int, error)
- func (c *WorkspaceCleaner) RunCleanups(ctx context.Context, sessionIDs []string)
- func (c *WorkspaceCleaner) UserSessions(ctx context.Context, ids []string) (map[string]bool, error)
Constants ¶
const ( // UserMachineGCLockKey makes one replica scan at a time. UserMachineGCLockKey = "weknora:sandbox:user-gc:lock" // UserMachineGCInterval is how often the container runs the scan. UserMachineGCInterval = time.Hour // UserMachineGCCursorKey remembers where a scan stopped, so the next one // continues instead of rereading the same oldest candidates. UserMachineGCCursorKey = "weknora:sandbox:user-gc:cursor" )
Variables ¶
var ErrMachineUnregistered = errors.New("sandboxalloc: shared machine is not registered")
ErrMachineUnregistered is returned when a user-mode assignment names a machine whose registry row is gone (its config was deleted). The session cannot run, and must never be mistaken for one with a machine of its own.
Functions ¶
func FromEntity ¶
func FromEntity(e *types.SessionSandboxAssignment, machine *types.SandboxUserMachine) allocation.Assignment
FromEntity converts a row back. machine is the registry row its digest names; it is required for a user-mode row and ignored otherwise.
func ToEntity ¶
func ToEntity(a allocation.Assignment, digest string) (*types.SessionSandboxAssignment, *types.SandboxUserMachine)
ToEntity converts an assignment into its row, and for a user-mode one the registry row of its machine (nil otherwise). digest names that machine.
Types ¶
type ConfigReader ¶
type ConfigReader interface {
GetByID(ctx context.Context, tenantID uint64, id string) (*types.TenantSandboxConfigEntity, error)
}
ConfigReader loads a sandbox config by its owning workspace.
type GCReport ¶
type GCReport struct {
Scanned, Reclaimed, Busy, Failed int
CleanupsDone, CleanupsFailed int
Skipped bool
}
GCReport is one scan's outcome, logged as metrics.
type PlaceRequest ¶
type PlaceRequest struct {
SessionTenantID uint64
SessionID string
ConfigTenantID uint64
ConfigID string
Purpose allocation.Purpose
}
PlaceRequest names the session and the config address the caller resolved as its candidate.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the allocation service.
func New ¶
func New( repo interfaces.SandboxAllocationRepository, configs ConfigReader, sessions SessionReader, namespace string, ) *Service
New wires the service. namespace must be the Redis binding namespace.
func (*Service) Digest ¶
func (s *Service) Digest(m allocation.UserMachine) string
Digest is the shared-machine digest of m in this deployment.
func (*Service) ForkAssignment ¶
func (s *Service) ForkAssignment(source allocation.Assignment, targetSessionID string) allocation.Assignment
ForkAssignment is the child's assignment: same mode, machine and config, fresh session id. The caller persists it with the forked session.
func (*Service) Lookup ¶
func (s *Service) Lookup(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)
Lookup returns the stored assignment without creating one. It always reads the database: callers use it to verify validity. A session retired before it was ever placed has no assignment.
func (*Service) Place ¶
func (s *Service) Place(ctx context.Context, req PlaceRequest) (allocation.Assignment, error)
Place returns the session's placement, recording it on first use together with the session's pin. The stored row always wins: neither the config switch, the candidate, nor the request identity can move a placed session.
func (*Service) Release ¶
func (s *Service) Release(ctx context.Context, sessionID string) (interfaces.RetiredSession, error)
Release retires one session.
func (*Service) Repo ¶
func (s *Service) Repo() interfaces.SandboxAllocationRepository
Repo is the allocation repository.
func (*Service) Retire ¶
func (s *Service) Retire(ctx context.Context, sessionIDs []string) ([]interfaces.RetiredSession, error)
Retire is the first step of deleting sessions (C5): one transaction, under the lock first placement takes, releases each session's assignment and schedules its workspace cleanup. A session without an assignment gets a tombstone, so no placement can land after the deletion began.
func (*Service) SessionAllocation ¶
func (s *Service) SessionAllocation(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)
SessionAllocation implements sandbox.AllocationDirectory.
func (*Service) UserMachinesByConfig ¶
func (s *Service) UserMachinesByConfig( ctx context.Context, configTenantID uint64, configID string, ) ([]allocation.UserMachine, error)
UserMachinesByConfig implements sandbox.AllocationDirectory.
func (*Service) VerifySessionAllocation ¶
func (s *Service) VerifySessionAllocation( ctx context.Context, sessionTenantID uint64, sessionID string, expected allocation.Assignment, ) error
VerifySessionAllocation implements sandbox.AllocationDirectory (C4, C15, C17). The row must still be live and name the expected address. For a shared machine, the digest recomputed from the session's CURRENT owner must be the stored one — the digest binds the owner, so this is stricter than comparing a snapshot — and the session's current auth source must still qualify. A retired session's row is released (or a tombstone), so the release check covers retirement.
type SessionReader ¶
type SessionReader interface {
GetByID(ctx context.Context, tenantID uint64, id string) (*types.Session, error)
}
SessionReader loads a session row.
type UserMachineGC ¶
type UserMachineGC struct {
// contains filtered or unexported fields
}
UserMachineGC reclaims user machines that have had no live session for their config's retention window, and retries queued directory deletes.
func NewUserMachineGC ¶
func NewUserMachineGC( svc *Service, cleaner *WorkspaceCleaner, configs ConfigReader, client redis.UniversalClient, ) *UserMachineGC
NewUserMachineGC wires the GC. A nil redis client runs unlocked, which is only correct for a single process (user mode is never enabled without Redis).
type UserMachineOps ¶
type UserMachineOps interface {
DeleteSessionWorkspaces(ctx context.Context, machine allocation.UserMachine, sessionIDs []string) ([]string, error)
ReclaimUserMachine(ctx context.Context, machine allocation.UserMachine,
check func(context.Context) (bool, error)) (bool, error)
}
UserMachineOps is the slice of *sandbox.SessionBoundManager the cleaner and the GC drive.
type WorkspaceCleaner ¶
type WorkspaceCleaner struct {
// contains filtered or unexported fields
}
WorkspaceCleaner gives a deleted user-mode session's directory back.
func NewWorkspaceCleaner ¶
func NewWorkspaceCleaner(svc *Service, resolver sandbox.TenantSandboxResolver) *WorkspaceCleaner
NewWorkspaceCleaner wires the cleaner. resolver resolves managers without the workspace kill switch: teardown must work while scripts are disabled.
func (*WorkspaceCleaner) DeleteSessions ¶
func (c *WorkspaceCleaner) DeleteSessions( ctx context.Context, tenantID uint64, idsByOwner map[string][]string, ) (map[string]bool, int, error)
DeleteSessions atomically removes the authorized session rows and records retirement and cleanup intent. Only committed retirements may trigger cleanup.
func (*WorkspaceCleaner) OwnerMachines ¶
func (c *WorkspaceCleaner) OwnerMachines( ctx context.Context, ownerTenantID uint64, ownerID string, ) ([]allocation.UserMachine, error)
OwnerMachines lists ownerID's machines in one tenant (0 = every tenant).
func (*WorkspaceCleaner) ReclaimOwner ¶
func (c *WorkspaceCleaner) ReclaimOwner(ctx context.Context, ownerID string)
ReclaimOwner gives back the deleted account's machines now; any it cannot reclaim yet are left to the GC's retention window.
func (*WorkspaceCleaner) ReclaimOwnerMachines ¶
func (c *WorkspaceCleaner) ReclaimOwnerMachines(ctx context.Context, machines []allocation.UserMachine)
ReclaimOwnerMachines deletes machines whose sessions were all just deleted by "delete all" or a user deletion, without waiting for the retention window. A machine with a live session, a cleared empty clock, or registered work is left to the GC.
func (*WorkspaceCleaner) ReleaseMachine ¶
func (c *WorkspaceCleaner) ReleaseMachine(ctx context.Context, m allocation.UserMachine) (bool, error)
ReleaseMachine deletes m now, when an admin chose to. Registered work and a live session still block it (false, nil): the same locked check ReclaimOwnerMachines uses decides, so a terminal is never cut and there is no second destroy path. A cleared empty_since is a session that joined, not an empty machine. Sessions on a refused machine keep their assignments.
func (*WorkspaceCleaner) ReleaseOwnerAssignments ¶
func (c *WorkspaceCleaner) ReleaseOwnerAssignments(ctx context.Context, ownerID string) error
ReleaseOwnerAssignments retires every session of a deleted account and releases their assignments (C5). It must succeed before the account is deleted: afterwards nothing could release them, and their machines would never become empty.
func (*WorkspaceCleaner) ReleaseSessions ¶
func (c *WorkspaceCleaner) ReleaseSessions(ctx context.Context, sessionIDs []string) (map[string]bool, error)
ReleaseSessions retires every session in sessionIDs in one transaction (C5): placement is closed for all of them, and each user-mode assignment is released with its cleanup task. handled names the sessions on a shared machine; the caller tears the others down as before. An error means nothing was retired; the caller must not delete the sessions.
func (*WorkspaceCleaner) RetryDue ¶
RetryDue retries queued directory deletes whose backoff has elapsed.
func (*WorkspaceCleaner) RunCleanups ¶
func (c *WorkspaceCleaner) RunCleanups(ctx context.Context, sessionIDs []string)
RunCleanups runs the due cleanup of these sessions now, one command per machine. Anything not done stays due for RetryDue.
func (*WorkspaceCleaner) UserSessions ¶
UserSessions classifies existing placements without retiring them. Retirement must commit with the session deletion, not with this pre-delete lookup.