Documentation
¶
Overview ¶
Package runner orchestrates a single test dispatch end-to-end: upload the test directory over SFTP, exec the per-OS entrypoint, collect logs and artifacts, and persist the run lifecycle to the runs.Store. The runner is invoked by the `testfleet run` CLI and by future automation surfaces.
Index ¶
- func Dispatch(ctx context.Context, deps Deps, req Request) (*runs.Run, error)
- func Execute(ctx context.Context, deps Deps, run *runs.Run, req Request, entrypoint string) *runs.Run
- func HashTestDir(dir string) (string, error)
- func Prepare(_ context.Context, deps Deps, req Request) (run *runs.Run, ready bool, hash string, entrypoint string, err error)
- func RemoteExecCommand(entrypoint, remoteRunDir, osName string) string
- func ResolveEntrypoint(testDir, osName string) (string, error)
- type Deps
- type DispatchError
- type EntrypointError
- type RealDispatcher
- type Request
- type SSHDispatcher
- type SSHSession
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Dispatch ¶
Dispatch orchestrates one run end-to-end.
Idempotency (Gap 6): if a run with the same (testDirHash, machine) already exists in pending/running state, that run-id is returned unchanged. Terminal runs (completed/failed/interrupted/timeout) do NOT dedupe — the caller asked again so we run again.
On preflight errors (missing entrypoint, machine busy with different hash) a *DispatchError is returned and NO run is persisted. Once the run is created the lifecycle is recorded into the store and (run, nil) is always returned; the caller branches on run.Status / run.ErrorCode.
func Execute ¶
func Execute(ctx context.Context, deps Deps, run *runs.Run, req Request, entrypoint string) *runs.Run
Execute performs the SSH work for a pending run: dial, upload, exec, download, finalize. It mutates the Run record in the store as it goes and returns the final run. Use this with a fresh pending run produced by Prepare(ready=true). Safe to invoke in a separate process (the _worker subcommand) for true async dispatch.
func HashTestDir ¶
HashTestDir returns a deterministic sha256 hex digest of the file contents rooted at dir. The algorithm: walk dir in sorted order, for each regular file write "<relative posix path>\0<sha256 of contents>\n" into a rolling hash; final hex digest is the testDirHash.
The path separator is normalized to "/" so the same tree hashes identically across OSes.
func Prepare ¶
func Prepare(_ context.Context, deps Deps, req Request) (run *runs.Run, ready bool, hash string, entrypoint string, err error)
Prepare runs the preflight + persistence steps of a dispatch:
- hash test dir
- resolve entrypoint (early-fail on missing run.ps1/run.sh)
- idempotency check (same testDirHash+machine, non-terminal → reuse)
- machine-busy check
- create pending Run record + state dirs
It does NOT touch SSH. The returned (run, ready=true) means a fresh pending run was created and the caller is responsible for invoking Execute to do the actual SSH work. (run, ready=false) means an existing non-terminal run was reused (idempotency hit) and no new work should be started.
On preflight errors a *DispatchError is returned and no run is persisted.
func RemoteExecCommand ¶
RemoteExecCommand builds the invocation string used over SSH given the entrypoint filename, the remote run directory, and the target OS.
The contract (per skills/testfleet/SKILL.md) is that the script writes artifacts to a *relative* `./artifacts/` directory — so we must run the script with its parent directory as the working directory. PowerShell `-File` does NOT change cwd, so we wrap with `Set-Location` first.
remoteRunDir is the POSIX-style directory the entrypoint lives in on the remote host (the SFTP server speaks POSIX even on Windows).
func ResolveEntrypoint ¶
ResolveEntrypoint returns the relative filename of the entrypoint script that the runner will invoke on the remote host. windows → run.ps1, anything else → run.sh. If the file does not exist locally the function returns an *EntrypointError carrying output.ErrMissingEntrypoint.
Types ¶
type Deps ¶
type Deps struct {
Store *runs.Store
SSH SSHDispatcher
Logger *slog.Logger
StateDir string // local state dir; logs/artifacts land under <StateDir>/runs/<run-id>/
SSHKey string
SSHUser string
SSHPort int // 0 → 22
KnownHostsPath string // path to known_hosts file
}
Deps is the dependency bundle passed to Dispatch.
type DispatchError ¶
DispatchError carries the structured error code returned to the CLI on pre-flight failures (missing entrypoint, machine busy, etc.). Once a Run is persisted, failures are recorded into the Run record itself and the (Run, nil) pair is returned; the caller checks Run.Status.
func (*DispatchError) Error ¶
func (e *DispatchError) Error() string
func (*DispatchError) Unwrap ¶
func (e *DispatchError) Unwrap() error
type EntrypointError ¶
EntrypointError indicates that the per-OS entrypoint script is missing from the test directory. Code is always output.ErrMissingEntrypoint so callers can surface it as the public error envelope.
func (*EntrypointError) Error ¶
func (e *EntrypointError) Error() string
type RealDispatcher ¶
type RealDispatcher struct{}
RealDispatcher implements SSHDispatcher against internal/ssh.
func (RealDispatcher) Dial ¶
func (RealDispatcher) Dial(_ context.Context, host string, port int, user, keyPath, knownHostsPath string) (SSHSession, error)
Dial opens an SSH client and wraps it in a realSession.
type Request ¶
type Request struct {
Machine machines.Machine
TestDir string // local path to the test directory
Timeout time.Duration // exec timeout (0 → no timeout)
Host string // host:port to dial; if empty, uses machine.Name
}
Request is the input to Dispatch.
type SSHDispatcher ¶
type SSHDispatcher interface {
Dial(ctx context.Context, host string, port int, user, keyPath, knownHostsPath string) (SSHSession, error)
}
SSHDispatcher is the narrow SSH/SFTP surface the runner needs.
Production wires RealDispatcher (over internal/ssh). Tests inject a fake session that returns canned stdout/stderr/exit-code so the runner orchestration can be exercised without an SSH server.
type SSHSession ¶
type SSHSession interface {
UploadDir(ctx context.Context, localDir, remoteDir string, timeout time.Duration) error
DownloadDir(ctx context.Context, remoteDir, localDir string, timeout time.Duration) error
Run(ctx context.Context, cmd string, stdin io.Reader, timeout time.Duration) (stdout, stderr []byte, exitCode int, err error)
Close() error
}
SSHSession is the per-connection surface used after Dial.
UploadDir / DownloadDir / Run each honour their own timeout (0 = no timeout) and the parent ctx. Run returns (stdout, stderr, exitCode, err); err is non-nil for transport-level failures and ctx cancellation. A non-zero exit code is surfaced via exitCode with nil err — mirroring internal/ssh.Run.