Documentation
¶
Overview ¶
Package localprocess implements the executor.Executor interface by forking child processes on the control-plane host.
This is the legacy execution path, extracted verbatim from the raw exec.Command calls that used to live in pkg/ui/server.go. It offers no isolation whatsoever: workloads inherit the control plane's user, filesystem, and network. It exists so the executor abstraction can be introduced without a behavior change, and so single-machine installs keep working with zero configuration — not because it is a good place to run untrusted agent workloads.
Deployments that must not execute agents on the control-plane host should bind their projects to an isolated executor and refuse to fall back here.
Behavior preserved from the pre-abstraction code:
- cmd.Dir is the project directory; an empty WorkDir inherits the server's cwd (the voice handler relies on this).
- The environment is inherited unless the Spec overrides it.
- stdout and stderr are merged into one os.Pipe, exactly as the live-log panel expects.
- No new process group: children stay in the control plane's group, so a Ctrl-C on an interactively-run server still reaches them.
- The real OS PID is reported, which is what the /proc scan in pkg/multiui (CloopRunPIDsInDir → the Stop button) matches against.
- Started workloads outlive the request that started them.
Index ¶
- Constants
- func Ensure(reg *executor.Registry) error
- type Executor
- func (e *Executor) AttachHandleStore(store executor.HandleStore)
- func (e *Executor) Capabilities() executor.Capabilities
- func (e *Executor) HandleStatuses(ctx context.Context) ([]executor.Status, error)
- func (e *Executor) Handles() []string
- func (e *Executor) HealthCheck(ctx context.Context) error
- func (e *Executor) HoldsLease(leaseID string) bool
- func (e *Executor) ID() string
- func (e *Executor) Kind() string
- func (e *Executor) Leases() []string
- func (e *Executor) Revocations() []executor.RevokeOutcome
- func (e *Executor) RevokeLease(ctx context.Context, req executor.RevokeRequest) executor.RevokeOutcome
- func (e *Executor) ScrubEnv(handleID string, keys []string) []string
- func (e *Executor) ScrubSpecSecrets(handleID string, bindings []executor.SecretBinding) []string
- func (e *Executor) Signal(ctx context.Context, handleID string, sig executor.Signal) error
- func (e *Executor) Start(ctx context.Context, spec executor.Spec) (executor.Handle, error)
- func (e *Executor) Status(ctx context.Context, handleID string) (executor.Status, error)
- func (e *Executor) Stream(ctx context.Context, handleID string) (<-chan executor.LogLine, error)
- func (e *Executor) SupportsRevocation() bool
Constants ¶
const DefaultID = "local"
DefaultID is the executor ID used by the zero-config singleton.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor is the host-process driver. Use New or Shared to obtain one.
func New ¶
New returns a new host-process executor with the given ID. An empty id defaults to DefaultID.
func (*Executor) AttachHandleStore ¶
func (e *Executor) AttachHandleStore(store executor.HandleStore)
AttachHandleStore installs the durable handle store and immediately reattaches to whatever it describes.
It is the *primary* wiring path for this driver, not the fallback it is for the container and Kubernetes drivers. Those are built from configuration and can take an Options.HandleStore at construction; this one is a process-wide singleton reached through Shared(), whose New(id) signature is fixed by every existing caller and takes no options at all. Adding a store-aware constructor beside it would leave two ways to build the same singleton and no way to make the Shared() path use the new one, so the store is installed after construction instead — which the boot order wanted anyway, since the state database that backs the store is opened long after the executor registry is built.
Calling it more than once is safe, including with a different store: see rehydrate for why re-adoption cannot produce a duplicate watcher.
A nil store is ignored rather than clearing the current one. "Forget how to find your processes" is not something a caller should be able to ask for by passing a zero value.
func (*Executor) Capabilities ¶
func (e *Executor) Capabilities() executor.Capabilities
Capabilities implements executor.Executor. Note SupportsResourceLimits is false: this driver cannot enforce CPU/memory caps, and Start rejects Specs that request them rather than silently ignoring the request.
SupportsWorkspaceProvisioning is false for a different reason, and it is an answer rather than a gap: this driver forks in the operator's own working directory, so the tree is already there. See Start.
func (*Executor) HandleStatuses ¶
HandleStatuses implements executor.Lister: a snapshot of every retained handle, running or recently finished. The Executors panel derives its "current load" column from this.
func (*Executor) Handles ¶
Handles returns the IDs of every handle this executor still knows about, newest first. Used for diagnostics and by tests.
func (*Executor) HealthCheck ¶
HealthCheck verifies the host can still hand out the file descriptors a Start needs. File-descriptor exhaustion is the realistic failure mode for a long-running control plane, and it manifests as os.Pipe failing.
func (*Executor) HoldsLease ¶
HoldsLease reports whether any process this executor tracks was started with material from leaseID.
func (*Executor) Revocations ¶
func (e *Executor) Revocations() []executor.RevokeOutcome
Revocations reports this executor's revocation log for the Secrets panel.
func (*Executor) RevokeLease ¶
func (e *Executor) RevokeLease(ctx context.Context, req executor.RevokeRequest) executor.RevokeOutcome
RevokeLease takes one lease's material back from every process holding it.
func (*Executor) ScrubEnv ¶
ScrubEnv drops the values of the named environment variables from this driver's retained copies of handleID's environment, returning the names it actually found.
Returning the names rather than a count is what lets a revocation ack say which credentials were reached. A caller that asked for three and got one back knows the other two were never here, which is a different situation from a scrub that failed.
Unknown handles and unknown keys are not errors: both mean "that material is not here", which is the end state a revocation wants.
func (*Executor) ScrubSpecSecrets ¶
func (e *Executor) ScrubSpecSecrets(handleID string, bindings []executor.SecretBinding) []string
ScrubSpecSecrets scrubs every environment variable a spec's secret bindings contributed, for callers that have the spec rather than a key list.
func (*Executor) Signal ¶
Signal implements executor.Executor. Signalling a finished handle is a no-op success: the caller wanted it stopped and it is stopped.
func (*Executor) Start ¶
Start forks the workload described by spec and returns its handle. The child outlives ctx: ctx only bounds the act of starting.
func (*Executor) Stream ¶
Stream implements executor.Executor. The returned channel first replays the bounded output backlog produced since Start, then delivers live chunks, then closes when the workload has finished and been reaped.
Cancelling ctx unsubscribes this consumer only; the workload and any other consumers are unaffected.
func (*Executor) SupportsRevocation ¶
SupportsRevocation reports whether a revocation issued now would be honoured. Always true: the process, its environment and its credential files are all on this machine, owned by this process.