Documentation
¶
Overview ¶
Package ssh provides SSH/SFTP transport for testfleet.
Index ¶
- Constants
- func Dial(host string, port int, user, keyPath, knownHostsPath string) (*ssh.Client, error)
- func DownloadDir(client *ssh.Client, remoteDir, localDir string) error
- func DownloadDirContext(ctx context.Context, client *ssh.Client, remoteDir, localDir string) error
- func DownloadDirWithTimeout(ctx context.Context, client *ssh.Client, remoteDir, localDir string, ...) error
- func RemoteWriteFile(ctx context.Context, client *ssh.Client, remotePath string, content []byte, ...) error
- func Run(ctx context.Context, client *ssh.Client, cmd string, stdin io.Reader) (stdoutBytes, stderrBytes []byte, exitCode int, err error)
- func RunWithTimeout(ctx context.Context, client *ssh.Client, cmd string, stdin io.Reader, ...) (stdout, stderr []byte, exitCode int, err error)
- func UploadDir(client *ssh.Client, localDir, remoteDir string) error
- func UploadDirContext(ctx context.Context, client *ssh.Client, localDir, remoteDir string) error
- func UploadDirWithTimeout(ctx context.Context, client *ssh.Client, localDir, remoteDir string, ...) error
- type DialError
- type ExecError
- type TransferError
Constants ¶
const DefaultTransferTimeout = 30 * time.Minute
DefaultTransferTimeout is the default deadline for SFTP UploadDir / DownloadDir when callers use the *WithTimeout helpers without specifying one.
Variables ¶
This section is empty.
Functions ¶
func Dial ¶
Dial opens an SSH client to host:port using public-key authentication.
knownHostsPath enforces strict host-key checking with an "accept-new" twist: when a host is encountered for the first time, its key is appended to the known_hosts file. If the file does not exist it is created (mode 0600 with parent dirs created at 0700). Subsequent key changes for an already-known host are rejected, matching OpenSSH's StrictHostKeyChecking=accept-new.
All failures (TCP connect, auth, host-key mismatch) are wrapped in a *DialError with ErrSSHFailed so callers can branch on output.ErrSSHFailed.
func DownloadDir ¶
DownloadDir is the inverse of UploadDir.
func DownloadDirContext ¶
DownloadDirContext is the context-aware variant of DownloadDir.
func DownloadDirWithTimeout ¶
func DownloadDirWithTimeout(ctx context.Context, client *ssh.Client, remoteDir, localDir string, timeout time.Duration) error
DownloadDirWithTimeout wraps DownloadDir with a per-call deadline.
func RemoteWriteFile ¶
func RemoteWriteFile(ctx context.Context, client *ssh.Client, remotePath string, content []byte, isWindows bool) error
RemoteWriteFile writes content to remotePath on the remote host.
On Linux/macOS targets (isWindows=false) the file is written via SFTP — straightforward and binary-safe.
On Windows targets (isWindows=true) we instead base64-encode content and pipe it through a PowerShell one-liner that converts it back to bytes:
powershell -NoProfile -ExecutionPolicy Bypass -Command
"$b='<base64>'; [System.IO.File]::WriteAllBytes('<path>',
[System.Convert]::FromBase64String($b))"
This avoids the escape-quoting hell of embedding arbitrary JSON / config payloads through PowerShell + OpenSSH's command translation layer (cf. qmd B3). It also sidesteps Windows OpenSSH's SFTP server quirks around partial writes for small files.
func Run ¶
func Run(ctx context.Context, client *ssh.Client, cmd string, stdin io.Reader) (stdoutBytes, stderrBytes []byte, exitCode int, err error)
Run executes cmd on the remote host. stdin (if non-nil) is fed to the remote process's standard input. The remote process is run inside an ssh.Session and its stdout/stderr are captured into memory.
When ctx is cancelled (timeout or explicit cancel) the session is closed, which kills the remote process; the returned error wraps ctx.Err().
exitCode reflects the remote process exit status:
- 0 on clean exit
- the remote-reported exit code on non-zero exit (no error returned — callers inspect exitCode themselves; the test fleet treats a non-zero exit as a normal test failure rather than an infra error).
- -1 when the remote process was killed by a signal or transport died.
func RunWithTimeout ¶
func RunWithTimeout(ctx context.Context, client *ssh.Client, cmd string, stdin io.Reader, timeout time.Duration) (stdout, stderr []byte, exitCode int, err error)
RunWithTimeout is Run with an explicit per-call deadline.
If timeout is <=0 the parent ctx is used unchanged. When the deadline elapses, the remote session is killed and the returned error wraps context.DeadlineExceeded with ErrTransferTimeout (matching the contract for SFTP transfers — the CLI surfaces "transfer_timeout" for any timed-out remote operation so the agent can branch on a single code).
func UploadDir ¶
UploadDir recursively uploads localDir to remoteDir on the remote host.
Behaviour:
- remote paths are always built with path.Join (POSIX "/") regardless of the local OS, because the remote SFTP server speaks POSIX paths even on Windows OpenSSH.
- missing intermediate remote directories are created (sftp.MkdirAll).
- file modes are preserved best-effort via os.Stat → Chmod.
- symlinks are followed (filepath.Walk default).
Failures are wrapped in *TransferError with ErrSSHFailed unless the context deadline fires, in which case ErrTransferTimeout is used.
func UploadDirContext ¶
UploadDirContext is the context-aware variant of UploadDir.
Types ¶
type TransferError ¶
TransferError wraps an sftp failure with a testfleet error code.
func (*TransferError) Error ¶
func (e *TransferError) Error() string
func (*TransferError) Unwrap ¶
func (e *TransferError) Unwrap() error