Help us shape the future of Go! Take the 2026 Go Developer Survey

runnerbusy

package
v0.2.21 Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package runnerbusy answers one question from ground truth rather than inference: is a GitHub Actions runner executing a job RIGHT NOW?

ephemerd otherwise only learns that a runner is busy by processing an `in_progress` webhook. That is inference ABOUT busy-ness, not an observation of it: anything that delays, drops or reorders a delivery — and a burst of same-label jobs makes all three likely — leaves a runner that is genuinely executing a build looking idle in the scheduler's ledger. Teardown decisions taken on that belief kill live builds.

The signal used here is the actions-runner's own process model. The listener process (`Runner.Listener`) runs for the whole life of the runner, so "a runner process exists" says nothing. The listener forks a `Runner.Worker` child ONLY while a job is executing, and reaps it when the job ends. "A worker exists" is therefore equivalent to "a job is running", and ephemerd — which owns the container, VM or process the runner lives in — can observe it locally: no network, no API budget, and immune to a missed or reordered webhook.

Every probe returns a State. Unknown is NOT idle. Callers MUST treat Unknown as "possibly busy" and fail safe; a probe that cannot answer never reports Idle.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupported = errors.New("runnerbusy: no local probe on this platform")

ErrUnsupported is returned by probes that have no implementation on the running platform. It is an explicit "I cannot answer" — never an implicit "not busy".

Functions

func IsWorkerProcess

func IsWorkerProcess(name string) bool

IsWorkerProcess reports whether a process identifier names the actions-runner's job worker.

The argument is whatever the platform's process listing yields for a process: argv[0] on Linux (an absolute path such as /home/runner/bin/Runner.Worker), /proc/<pid>/comm as a fallback, or the HCS ImageName on Windows (Runner.Worker.exe). Matching is on the base name, case-insensitively, with a .exe suffix stripped, so one rule covers all three.

Runner.Listener is deliberately NOT matched: it is alive for the whole life of the runner, including while the runner sits idle waiting for a job, so matching it would make every runner look permanently busy.

Types

type ContainerTask

type ContainerTask interface {
	ID() string
	Pids(context.Context) ([]client.ProcessInfo, error)
}

ContainerTask is the slice of containerd's client.Task that the container probe needs. Declared as an interface so the probe can be unit-tested without a containerd daemon.

ID returns the container ID (the init task's ID is the container's). Pids lists the processes in the container.

type State

type State int

State is a probe's verdict about a runner.

const (
	// Unknown means the probe could not determine the runner's state:
	// the platform has no local probe, the runtime refused the query, or
	// the answer was empty in a way that cannot be distinguished from a
	// failure. Callers must treat it as "possibly busy".
	Unknown State = iota

	// Idle means the probe positively observed that no job is executing:
	// the runner's process list was read successfully and contained no
	// worker. Only this verdict makes teardown safe.
	Idle

	// Busy means the probe positively observed a worker process, i.e. a
	// job is executing right now. Destroying the runner would kill it.
	Busy
)

func ContainerBusy

func ContainerBusy(ctx context.Context, t ContainerTask, log *slog.Logger) (State, error)

ContainerBusy reports whether the runner inside a containerd-managed Linux container is executing a job.

runc leaves container processes visible in the host's PID namespace, so the PIDs containerd reports are readable under /proc on the host. For each one we read argv[0] (falling back to the kernel's comm) and look for the runner's worker child.

Failure modes all resolve to Unknown, never Idle:

  • the task query fails (shim gone, containerd restarting)
  • the task reports no processes at all, which is indistinguishable from a listing that raced container teardown
  • every /proc read failed, which means we are not in a position to see the container's processes (foreign PID namespace, hardened /proc) rather than that the container is quiet

func (State) String

func (s State) String() string

Jump to

Keyboard shortcuts

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