Files
rvbox/internal/client/supervisor/supervisor.go
T

118 lines
3.2 KiB
Go

// Package supervisor defines the narrow process-control seam shared by the
// client runtime and platform implementations. It intentionally contains no
// operating-system handles or process APIs.
package supervisor
import (
"context"
"errors"
"time"
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
"github.com/rvbox/rvbox/internal/domain"
)
var (
ErrInvalidStartSpec = errors.New("invalid supervisor start specification")
ErrUnsupported = errors.New("supervisor operation is unsupported")
)
type SignalKind uint8
const (
SignalTerm SignalKind = iota + 1
SignalKill
)
// StartSpec is already validated at the protocol boundary. The supervisor
// still checks the identity/revision/source invariants because it is a second
// durability boundary and may be called after a restart.
type StartSpec struct {
IssueUUID domain.UUID
CommandRevision uint64
Execution *rvboxv1.ExecutionSpec
// ScriptBody is the verified, durable body for Execution.script. It is
// supplied by the client spool only after the declared digest/length have
// been checked; command_text requests leave it empty.
ScriptBody []byte
WorkingDirectory string
Environment map[string]string
ExecutionProfiles []string
}
func (spec StartSpec) Validate() error {
if _, err := domain.ParseUUIDv7(spec.IssueUUID.String()); err != nil || spec.CommandRevision == 0 || spec.Execution == nil || spec.Execution.Source == nil {
return ErrInvalidStartSpec
}
if spec.WorkingDirectory == "" {
return ErrInvalidStartSpec
}
return nil
}
// EffectiveIdentity is immutable process evidence captured before launch.
// Empty user/session fields mean a Session 0 service context.
type EffectiveIdentity struct {
Context string
SessionID uint32
UserSID string
LogonSID string
Elevated bool
Integrity string
}
type Process interface {
IssueUUID() domain.UUID
Identity() EffectiveIdentity
// ReadOutput returns the next bounded stdout/stderr chunk. It continues
// until both child pipes reach EOF, so Wait never reports a terminal
// result before the captured output has drained.
ReadOutput(context.Context) (OutputChunk, error)
Wait(context.Context) (ExitStatus, error)
WriteStdin(context.Context, []byte, bool) error
CloseStdin(context.Context) error
}
// OutputChunk is intentionally raw. Compression, quota admission, local
// ordering, and wire sequencing belong to the client spool rather than the
// operating-system supervisor.
type OutputChunk struct {
Stream rvboxv1.StreamKind
Data []byte
}
type ExitStatus struct {
Code int32
Signaled bool
Signal SignalKind
StartedAt time.Time
FinishedAt time.Time
OutputDrained bool
Output bool
}
type SignalOutcome struct {
Delivered bool
Escalated bool
Detail string
ObservedAt time.Time
}
type ResourceSnapshot struct {
CPUTime time.Duration
ResidentBytes uint64
IOReadBytes uint64
IOWriteBytes uint64
ProcessCount uint64
ObservedAt time.Time
Complete bool
Detail string
}
type Supervisor interface {
Start(context.Context, StartSpec) (Process, error)
Signal(context.Context, Process, SignalKind) (SignalOutcome, error)
Snapshot(context.Context, Process) (ResourceSnapshot, error)
StopAll(context.Context) error
}