Documentation
¶
Overview ¶
Package iago provides a framework for running tasks on remote hosts.
Index ¶
- Variables
- func CleanPath(path string) string
- func Collect[T any](g Group, name string, fn func(context.Context, Host) (T, error)) (map[string]T, error)
- func DirExists(ctx context.Context, host Host, path string) (bool, error)
- func Expand(h Host, s string) string
- func FileExists(ctx context.Context, host Host, path string) (bool, error)
- func GetIntVar(host Host, key string) int
- func GetStringVar(host Host, key string) string
- func Ignore(e error)
- func Output(ctx context.Context, host Host, cmd string) (string, error)
- func Panic(e error)
- func ParseHosts(spec, configFile string) ([]string, error)
- func ParseSSHConfig(configFile string) (*sshConfig, error)
- func Quote(s string) string
- func UploadFile(ctx context.Context, host Host, localPath, remotePath string, perm Perm) error
- type CmdRunner
- type Download
- type DownloadDir
- type ErrorHandler
- type Errors
- type ExitStatus
- type Group
- type GroupOption
- type Host
- type Path
- type Perm
- type ProgressFunc
- type Shell
- type TaskError
- type Upload
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotAbsolute is returned when a path is relative, but was expected to be absolute. ErrNotAbsolute = errors.New("not an absolute path") // ErrNotRelative is returned when a path is absolute, but was expected to be relative. ErrNotRelative = errors.New("not a relative path") )
var ConnectTimeout time.Duration
ConnectTimeout is the default timeout for establishing an SSH connection. It is used when a host does not set ConnectTimeout in the SSH config. The zero value means no timeout, which preserves the prior behavior of ssh.ClientConfig.Timeout. Callers can set this to a non-zero value to apply a default dial timeout across all hosts.
var DefaultTimeout = 30 * time.Second
DefaultTimeout is the default timeout for an action.
Functions ¶
func Collect ¶
func Collect[T any](g Group, name string, fn func(context.Context, Host) (T, error)) (map[string]T, error)
Collect runs fn concurrently on every host in g and returns each host's result keyed by host name. A host whose fn returns a non-nil error contributes no entry to the map; those errors are joined and returned as Collect's second value (nil when every host succeeded).
func DirExists ¶
DirExists reports whether path is a directory on host, checked with a POSIX `test -d`. See FileExists for the exit-status contract.
func Expand ¶
Expand expands any environment variables in the string 's' using the environment of the host 'h'.
func FileExists ¶
FileExists reports whether path exists on host and is not a directory, checked with a POSIX `test -f`. It returns false, nil when the remote command determines the path fails the test (exit status 1); any other failure (a transport error, a missing shell) is returned as an error.
func GetStringVar ¶
GetStringVar gets a string variable from the host.
func Output ¶
Output runs cmd on host as a shell command and returns its captured standard output. It is a convenience wrapper around Shell for the common case of wanting a command's output as a string rather than streaming it to a caller-provided writer.
func ParseHosts ¶
ParseHosts resolves a comma-separated host specification to a slice of SSH host aliases. Each token in the spec is handled as follows:
- A PREFIX[lo-hi]SUFFIX token with numeric bounds is expanded to individual aliases (e.g. "bb[1-30]" → bb1, bb2, …, bb30) without consulting the SSH config.
- A token containing *, ?, or a non-numeric [...] bracket expression is treated as a glob and matched against the non-wildcard Host entries read from configFile. Wildcard SSH stanzas (e.g. "Host bb*") are skipped because they do not enumerate specific host names.
- Any other token is returned verbatim as a literal alias.
If configFile is empty, ~/.ssh/config is used. The config file is parsed at most once, only when a glob token is encountered.
func ParseSSHConfig ¶
ParseSSHConfig returns a ssh configuration object that can be used to create a ssh.ClientConfig for a given host alias.
func Quote ¶
Quote wraps s in single quotes so it is safe to embed as one argument in a Shell command run on a POSIX shell. An embedded single quote is escaped using the `'\”` idiom: end the quoted string, emit an escaped quote, and resume quoting.
func UploadFile ¶
UploadFile uploads the local file at localPath to remotePath on host with the given permissions. It is a convenience wrapper around Upload for a single file, handling the Path conversion of an already-absolute local path and an absolute remote path so callers do not repeat that boilerplate at every call site.
Types ¶
type CmdRunner ¶
type CmdRunner interface {
Run(cmd string) error
RunContext(ctx context.Context, cmd string) error
Start(cmd string) error
Wait() error
StdinPipe() (io.WriteCloser, error)
StdoutPipe() (io.ReadCloser, error)
StderrPipe() (io.ReadCloser, error)
}
CmdRunner defines an interface for running commands on remote hosts. This interface is based on the "exec.Cmd" struct.
type Download ¶
Download downloads a file or directory from a remote host. For each host in a group, a subdirectory named after the host is created under Dest so that results from multiple hosts do not collide. Use DownloadDir when you are downloading from a single host and want the remote directory's contents placed directly into the local destination.
type DownloadDir ¶
type DownloadDir struct {
Src Path
Dest Path
Progress ProgressFunc
}
DownloadDir downloads the contents of a remote directory directly into a local destination via SFTP, without adding a per-host subdirectory. Use this instead of Download when fetching from a single host and direct placement is desired. Progress, if non-nil, is called with each chunk of bytes transferred.
type ErrorHandler ¶
type ErrorHandler func(error)
ErrorHandler is a function that handles errors from actions.
type Errors ¶
type Errors struct {
// contains filtered or unexported fields
}
Errors is an ErrorHandler that accumulates the errors passed to it for later retrieval instead of panicking like Panic. It is the collecting counterpart to Panic and Ignore, for a caller that runs a task on every host and then acts on the joined result:
var errs iago.Errors
g.ErrorHandler = errs.Handle
g.Run("task", task)
return errs.Err()
Group.Run invokes the handler sequentially, so within a single Run the collector is uncontended; the mutex makes it safe to share one Errors across concurrent Runs as well. The zero value is ready to use.
func (*Errors) Err ¶
Err returns the recorded errors joined with errors.Join, or nil when none were recorded.
func (*Errors) Handle ¶
Handle records err. It satisfies the ErrorHandler signature, so it can be assigned to Group.ErrorHandler or passed to WithErrorHandler.
type ExitStatus ¶
ExitStatus is implemented by an error carrying a remote process's exit status, such as the golang.org/x/crypto/ssh package's *ssh.ExitError returned by Shell.Apply when the remote command runs to completion but exits non-zero. errors.AsType[iago.ExitStatus](err) extracts it from a wrapped error (for example a TaskError from Group.Run) without the caller importing golang.org/x/crypto/ssh, and distinguishes "the command ran and exited non-zero" from "the command never completed" (a dial failure, a dropped connection, and the like, none of which implement it).
type Group ¶
type Group struct {
Hosts []Host
ErrorHandler ErrorHandler
Timeout time.Duration
// DialErrors holds per-alias dial failures from [NewSSHGroup] when
// [FailFast] is not set. A nil map means all aliases connected successfully.
DialErrors map[string]error
// contains filtered or unexported fields
}
Group is a group of hosts.
func NewSSHGroup ¶
func NewSSHGroup(hostAliases []string, sshConfigFile string, opts ...GroupOption) (group Group, err error)
NewSSHGroup returns a new ssh group from the given host aliases. The sshConfigFile argument specifies the ssh config file to use. If sshConfigFile is empty, the default configuration files will be used: ~/.ssh/config.
The host aliases should be defined in the ssh config file, and the config file should contain the necessary information to connect to the hosts without a passphrase. This usually means setting up the ssh-agent with the necessary keys beforehand (and entering the passphrase), or specifying the passphrase-less key to use with the IdentityFile option. Moreover, the config file should specify whether or not to use strict host key checking using the StrictHostKeyChecking option. If strict host key checking is enabled, the ssh server's host keys should be present in the known_hosts files specified by UserKnownHostsFile (the default known_hosts files will be used if this option is not specified).
The specified hosts must all contain an authorized_keys file containing the public key of the user running this program.
When several aliases share the same ProxyJump spec, a single TCP/SSH connection to the jump host is dialed once and reused for every target tunnelled through it. This mirrors what OpenSSH's ControlMaster provides for the system ssh client and avoids opening one proxy connection per target alias. The shared jump clients are owned by the returned Group and closed by Group.Close.
By default, dial failures are collected in Group.DialErrors instead of aborting the call. If no hosts connect successfully, an error is returned. Pass FailFast to return an error if any target fails. Pass DialConcurrency to dial target hosts concurrently; jump connections are always established sequentially first so at most one TCP connection is made to each jump host.
type GroupOption ¶
type GroupOption func(*groupConfig)
GroupOption configures how NewSSHGroup dials hosts.
func DialConcurrency ¶
func DialConcurrency(n int) GroupOption
DialConcurrency returns a GroupOption that sets the maximum number of target hosts dialed concurrently inside NewSSHGroup. Values less than 2 leave dialing sequential (the default). Jump connections are always established sequentially before concurrent target dialing begins, so there is at most one TCP connection to each jump host regardless of n.
func FailFast ¶
func FailFast() GroupOption
FailFast returns a GroupOption that makes NewSSHGroup stop and return an error if any dial fails. Targets that have not yet been dialed when the first failure is observed are skipped; combined with DialConcurrency this is best-effort, as dials already in flight still run to completion. Without this option, NewSSHGroup collects all dial errors in Group.DialErrors and returns only the successfully connected hosts.
func ForwardAgent ¶
func ForwardAgent() GroupOption
ForwardAgent returns a GroupOption that forces SSH agent forwarding for every host in the group, regardless of the ForwardAgent setting in the SSH config. This is equivalent to passing -A to the ssh command-line tool: each session opened on a dialed host will request agent forwarding so that the remote process can authenticate onward to other hosts using the local agent.
func KeepAlive ¶
func KeepAlive(interval time.Duration) GroupOption
KeepAlive returns a GroupOption that sends an SSH keepalive request on every dialed connection every interval. golang.org/x/crypto/ssh does not honor the OpenSSH ServerAliveInterval config directive, so without this a connection left idle (for example, a control channel streaming a long, quiet remote run) can be silently dropped by a NAT or firewall idle timeout. The periodic traffic keeps the mapping alive; if a keepalive fails, the connection is closed so any in-flight session unblocks promptly instead of hanging. An interval of zero or less disables keepalives (the default).
func WithErrorHandler ¶
func WithErrorHandler(h ErrorHandler) GroupOption
WithErrorHandler returns a GroupOption that sets the ErrorHandler of the group returned by NewSSHGroup. Without it the group defaults to Panic, matching NewGroup. Pass a shared Errors collector's Handle method to gather task errors instead of panicking:
var errs iago.Errors
g, err := iago.NewSSHGroup(aliases, cfg, iago.WithErrorHandler(errs.Handle))
// ...
g.Run("task", task)
return errs.Err()
type Host ¶
type Host interface {
// Name returns the name of this host.
Name() string
// Address returns the address of the host.
Address() string
// GetEnv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
GetEnv(key string) string
// GetFS returns the file system of the host.
GetFS() fs.FS
// NewCommand returns a new command runner.
NewCommand() (CmdRunner, error)
// Close closes the connection to the host.
Close() error
// SetVar sets a host variable with the given key and value
SetVar(key string, val any)
// GetVar gets the host variable with the given key.
// Returns (val, true) if the variable exists, (nil, false) otherwise.
GetVar(key string) (val any, ok bool)
}
Host is a connection to a remote host.
type Path ¶
type Path struct {
// contains filtered or unexported fields
}
Path is a path to a file or directory, relative to the prefix.
func NewPath ¶
NewPath returns a new Path struct. prefix must be an absolute path, and path must be relative to the prefix.
func NewPathFromAbs ¶
NewPathFromAbs returns a new Path struct from an absolute path.
type Perm ¶
type Perm struct {
// contains filtered or unexported fields
}
Perm describes the permissions that should be used when creating files or directories. Perm can use different permissions for files and directories. By default, it uses 644 for files and 755 for directories. If a file permission is specified by using NewPerm(), the WithDirPerm() method may be called to modify the directory permissions.
func NewPerm ¶
NewPerm returns a Perm with the requested file permission. Note that this will also set the directory permission. If a different directory permission is desired, you must call WithDirPerm on the returned Perm also.
func (Perm) GetDirPerm ¶
GetDirPerm returns the current directory permission, or the current file permission, or 755 if no permissions were set.
func (Perm) GetFilePerm ¶
GetFilePerm returns the current file permission, or 644 if no file permission was set.
type ProgressFunc ¶
type ProgressFunc func(n int64)
ProgressFunc is called during a file transfer to report incremental progress. n is the number of bytes just transferred.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package iagotest provides utilities for external libraries to test using the iago package.
|
Package iagotest provides utilities for external libraries to test using the iago package. |
|
Package sftpfs provides a filesystem interface to an SFTP server using the github.com/pkg/sftp package.
|
Package sftpfs provides a filesystem interface to an SFTP server using the github.com/pkg/sftp package. |