Documentation
¶
Overview ¶
Package gomoteclient is an ergonomic, concurrency-safe wrapper around the gomote command-line tool (golang.org/x/build/cmd/gomote). It is intended to replace ad-hoc shelling out to the gomote binary with strongly-typed, context-aware, retry-capable command construction.
Transport ¶
The client wraps the gomote CLI; it does not speak the coordinator gRPC protocol directly. The CLI handles authentication, so callers only need a working, authenticated gomote on PATH (or allow the client to bootstrap one). Command execution goes through the unexported runner interface, which both isolates the os/exec dependency for testing and leaves room for an alternative backend in the future.
Security posture ¶
The client never invokes a shell: commands are executed by absolute path with an explicit argument vector, eliminating the shell-injection surface that arises from string concatenation. Inputs that become argv elements (builder types, instance IDs, environment keys) are validated to prevent flag smuggling. The subprocess receives an explicit, curated environment rather than inheriting the host's, and Client.Info redacts secret-looking values.
Bootstrapping (building gomote from source when it is missing) is opt-in via Config.AllowBootstrap. Builds run in an isolated sandbox with module checksum verification left enabled. Pinning Config.GomoteVersion yields a reproducible build; leaving it empty resolves "@latest", which is convenient but not reproducible and is logged as a warning.
Example ¶
c, err := gomoteclient.New(ctx, gomoteclient.Config{})
if err != nil {
log.Fatal(err)
}
inst, err := c.Create(ctx, gomoteclient.CreateOptions{BuilderType: "gotip-linux-amd64"})
if err != nil {
log.Fatal(err)
}
defer inst.Destroy(ctx)
res, err := inst.Run(ctx, gomoteclient.RunOptions{
Dir: "tmp/sharded",
Command: "go",
Args: []string{"test", "-bench", "."},
})
_ = res
Coverage ¶
The wrapper exposes create, list, run, push, destroy, ping, ls, rm, the put/puttar/putbootstrap upload family, and gettar download. Builder types are the named BuilderType; discover the authoritative set for the connected coordinator with Client.BuilderTypes rather than hard-coding names. The coordinator address is selectable via Config.Server. gomote's interactive (ssh, rdp) and group subcommands are intentionally out of scope: they do not fit a captured-output, single-instance model.
Index ¶
- Variables
- type BuilderType
- type Client
- func (c *Client) BuilderTypes(ctx context.Context) ([]BuilderType, error)
- func (c *Client) Create(ctx context.Context, opt CreateOptions) (*Instance, error)
- func (c *Client) Info() Info
- func (c *Client) Instance(id string) (*Instance, error)
- func (c *Client) List(ctx context.Context) ([]InstanceInfo, error)
- type CommandError
- type Config
- type CreateOptions
- type EnvVar
- type GetOptions
- type Info
- type Instance
- func (in *Instance) BuilderType() BuilderType
- func (in *Instance) Destroy(ctx context.Context) error
- func (in *Instance) Get(ctx context.Context, opt GetOptions) error
- func (in *Instance) ID() string
- func (in *Instance) Ls(ctx context.Context, opt LsOptions) ([]string, error)
- func (in *Instance) Ping(ctx context.Context) error
- func (in *Instance) Push(ctx context.Context) error
- func (in *Instance) Put(ctx context.Context, opt PutOptions) error
- func (in *Instance) PutBootstrap(ctx context.Context) error
- func (in *Instance) PutTar(ctx context.Context, opt PutTarOptions) error
- func (in *Instance) Remove(ctx context.Context, paths ...string) error
- func (in *Instance) Run(ctx context.Context, opt RunOptions) (RunResult, error)
- type InstanceInfo
- type JitterMode
- type LsOptions
- type Option
- func WithBinaryPath(path string) Option
- func WithBootstrap(version string) Option
- func WithDefaultTimeout(d time.Duration) Option
- func WithEnv(env []string) Option
- func WithLogger(l *slog.Logger) Option
- func WithRetryPolicy(p RetryPolicy) Option
- func WithSandboxDir(dir string) Option
- func WithServer(addr string) Option
- type PutOptions
- type PutTarOptions
- type RetryPolicy
- type RunOptions
- type RunResult
- type Source
Constants ¶
This section is empty.
Variables ¶
var ErrInstanceNotFound = errors.New("gomoteclient: instance not found")
ErrInstanceNotFound indicates that the coordinator does not know the target instance: it was never created, already destroyed, or its lease expired. Callers can test for it with errors.Is. It is joined with the underlying *CommandError, so errors.As can still recover the command details.
Functions ¶
This section is empty.
Types ¶
type BuilderType ¶
type BuilderType string
BuilderType is a gomote builder (host) type such as "gotip-linux-amd64". The authoritative set is dynamic and version-dependent; discover it at runtime with Client.BuilderTypes (which runs "gomote create -list") rather than hard-coding names. The named type exists to keep builder identifiers from being confused with arbitrary strings at call sites and to centralize validation.
func (BuilderType) String ¶
func (b BuilderType) String() string
String returns the builder type as a string.
func (BuilderType) Validate ¶
func (b BuilderType) Validate() error
Validate reports whether b is syntactically a valid builder type. It checks shape only; it does not confirm the type exists on the coordinator.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a concurrency-safe handle to a resolved gomote binary together with the policy used to invoke it. A Client is safe for use by multiple goroutines.
func New ¶
New resolves the gomote binary and returns a ready Client. Resolution order is Config.BinaryPath, then PATH, then bootstrap (only if Config.AllowBootstrap is set). New does not contact the coordinator. The provided context bounds the bootstrap build, if one occurs.
func NewClient ¶
NewClient is the functional-options constructor: it assembles a Config from opts and delegates to New. It is equivalent to New with a Config literal and is provided for call sites that prefer a fluent style.
func (*Client) BuilderTypes ¶
func (c *Client) BuilderTypes(ctx context.Context) ([]BuilderType, error)
BuilderTypes returns the builder types the coordinator currently offers, by running "gomote create -list". The result is authoritative for the connected server and should be preferred over hard-coded names.
func (*Client) Create ¶
Create provisions a new buildlet and returns a handle to it. Create is not retried by default (see CreateOptions.RetrySafe).
func (*Client) Info ¶
Info returns a snapshot of the client's binary, sandbox, tracked instances, and (redacted) subprocess environment. It performs no command execution.
type CommandError ¶
type CommandError struct {
// Args is the (secret-redacted) argument vector that was executed.
Args []string
// ExitCode is the process exit code, or -1 if the process did not start.
ExitCode int
// Stderr is the captured standard error of the command.
Stderr string
// Err is the underlying execution error.
Err error
}
CommandError reports a failed gomote invocation. It preserves the captured stderr from the CLI and unwraps to the underlying os/exec error, so callers can use errors.As to inspect *exec.ExitError.
func (*CommandError) Error ¶
func (e *CommandError) Error() string
Error implements error, including the redacted command and stderr tail.
func (*CommandError) Unwrap ¶
func (e *CommandError) Unwrap() error
Unwrap returns the underlying execution error.
type Config ¶
type Config struct {
// BinaryPath, if set, is an explicit path to a gomote binary. It is
// validated and resolved to an absolute path. When empty, the client
// resolves the binary from PATH and then, if AllowBootstrap is true,
// bootstraps one into SandboxDir.
BinaryPath string
// SandboxDir is the directory used to build and cache a bootstrapped
// gomote. When empty it defaults to <os.UserCacheDir>/gomoteclient.
SandboxDir string
// GomoteVersion is the module version installed during bootstrap, for
// example a tag or pseudo-version of golang.org/x/build. When empty,
// bootstrap resolves "@latest" (not reproducible; logged as a warning).
GomoteVersion string
// AllowBootstrap permits the client to download and build gomote when it
// is not otherwise available. It defaults to false: a missing binary is
// then a hard error rather than an implicit network build.
AllowBootstrap bool
// Server overrides the coordinator address (gomote's global -server flag,
// for example "gomote.golang.org:443"). When empty, gomote's built-in
// default is used. The value is validated to reject argument injection.
Server string
// Retry governs transient-failure handling. The zero value is replaced by
// DefaultRetryPolicy; individual zero fields are filled with defaults.
Retry RetryPolicy
// Env is the explicit environment passed to the gomote subprocess. When
// nil, a curated allowlist is derived from the host environment (see the
// package documentation). Provide a value to fully control isolation.
Env []string
// DefaultTimeout bounds a single command attempt when neither the caller's
// context nor RetryPolicy.PerAttempt sets a deadline. Zero means no
// implicit deadline.
DefaultTimeout time.Duration
// Logger receives structured, secret-redacted diagnostics. When nil, logs
// are discarded.
Logger *slog.Logger
}
Config configures a Client. The zero value is usable: it resolves gomote from PATH (or Config.BinaryPath), applies DefaultRetryPolicy, derives a curated subprocess environment, and discards log output. Bootstrapping is disabled unless AllowBootstrap is set.
type CreateOptions ¶
type CreateOptions struct {
// BuilderType is the gomote builder type, e.g. "gotip-linux-amd64". Required.
BuilderType BuilderType
// SetUpEnv are environment variables applied to the new instance via -e.
SetUpEnv []EnvVar
// Setup requests that gomote push GOROOT and build the Go toolchain on the
// new instance (the -setup flag). It is off by default.
Setup bool
// RetrySafe permits transparent retries of Create. It is off by default
// because a retried create can leak buildlets; enable it only when the
// caller cleans up orphans.
RetrySafe bool
}
CreateOptions configures Client.Create.
type GetOptions ¶
type GetOptions struct {
// Dir is the remote directory to archive (-dir). Optional.
Dir string
// Output is the local path to write the downloaded .tar.gz to. Required.
Output string
}
GetOptions configures Instance.Get.
type Info ¶
type Info struct {
// BinaryPath is the absolute path to the resolved gomote binary.
BinaryPath string
// BinarySource indicates how the binary was located.
BinarySource Source
// SandboxDir is the configured sandbox directory (may be empty).
SandboxDir string
// SandboxReady reports whether the binary was built into the sandbox.
SandboxReady bool
// GomoteVersion is the module version when known (bootstrap builds), else "".
GomoteVersion string
// ActiveInstances are the IDs of instances created via this Client that
// have not been destroyed, sorted lexically.
ActiveInstances []string
// Env is the redacted environment passed to the gomote subprocess.
Env []string
}
Info is a point-in-time snapshot of a Client's state. The Env field is redacted: values of secret-looking keys are masked.
type Instance ¶
type Instance struct {
// contains filtered or unexported fields
}
Instance is a handle to a single created gomote buildlet. It is safe for concurrent use; its methods are stateless apart from the shared Client.
func (*Instance) BuilderType ¶
func (in *Instance) BuilderType() BuilderType
BuilderType returns the builder type the instance was created from. It is the zero value for instances obtained via Client.Instance.
func (*Instance) Destroy ¶
Destroy tears down the instance. It is idempotent: an already-gone instance is treated as success.
func (*Instance) Get ¶
func (in *Instance) Get(ctx context.Context, opt GetOptions) error
Get downloads files from the instance as a tar archive (gomote gettar) and writes them to GetOptions.Output.
gomote gettar writes "<instance>.tar.gz" into its working directory rather than to stdout, so Get runs it inside a private temporary directory and then moves the result to Output. This keeps the download off the caller's working directory and out of any retry-duplicated state.
func (*Instance) Ls ¶
Ls lists the contents of a directory on the instance (gomote ls) and returns the non-empty output lines.
func (*Instance) Ping ¶
Ping reports whether the instance is alive and reachable (gomote ping). A nil return means healthy; a missing instance reports errors.Is(err, ErrInstanceNotFound).
func (*Instance) Put ¶
func (in *Instance) Put(ctx context.Context, opt PutOptions) error
Put uploads a single file to the instance.
func (*Instance) PutBootstrap ¶
PutBootstrap places the bootstrap toolchain on the instance (gomote putbootstrap), a prerequisite for building Go from source on some hosts.
func (*Instance) PutTar ¶
func (in *Instance) PutTar(ctx context.Context, opt PutTarOptions) error
PutTar uploads and extracts a tar archive onto the instance.
type InstanceInfo ¶
type InstanceInfo struct {
ID string
Raw string // the full, unparsed line as printed by gomote list
}
InstanceInfo describes an instance reported by Client.List.
type JitterMode ¶
type JitterMode int
JitterMode selects how backoff delays are randomized.
const ( // FullJitter (the default) draws each delay uniformly from // [0, cappedExponentialDelay], which is the most effective scheme for // avoiding synchronized retries across concurrently fanned-out callers. FullJitter JitterMode = iota // NoJitter uses the capped exponential delay directly. NoJitter )
type LsOptions ¶
type LsOptions struct {
// Dir is the remote directory to list, relative to the work dir. Optional.
Dir string
// Recursive lists directories recursively (-R).
Recursive bool
// Digest requests file digests (-d).
Digest bool
// Skip is a list of relative directories to skip (-skip), joined with ",".
Skip []string
}
LsOptions configures Instance.Ls.
type Option ¶
type Option func(*Config)
Option configures a Client built by NewClient. Options are a thin, fluent alternative to constructing a Config literal; they compose left to right.
func WithBinaryPath ¶
WithBinaryPath pins an explicit gomote binary instead of resolving from PATH.
func WithBootstrap ¶
WithBootstrap enables building gomote from source when it is missing, pinning the given module version (empty resolves "@latest").
func WithDefaultTimeout ¶
WithDefaultTimeout bounds each command attempt when the caller sets no deadline.
func WithLogger ¶
WithLogger directs structured, secret-redacted diagnostics to l.
func WithRetryPolicy ¶
func WithRetryPolicy(p RetryPolicy) Option
WithRetryPolicy installs a custom retry policy.
func WithSandboxDir ¶
WithSandboxDir sets the directory used to cache a bootstrapped gomote.
func WithServer ¶
WithServer overrides the coordinator address (the global -server flag).
type PutOptions ¶
type PutOptions struct {
// Source is the local file to upload. Required.
Source string
// Dest is the destination path on the instance. Optional.
Dest string
// Mode optionally sets the destination Unix file mode (-mode). When zero,
// gomote defaults to the source file's mode.
Mode os.FileMode
}
PutOptions configures Instance.Put.
type PutTarOptions ¶
type PutTarOptions struct {
// Dir is the destination directory on the instance (-dir).
Dir string
// File is the local .tar.gz archive to upload. Required.
File string
}
PutTarOptions configures Instance.PutTar.
type RetryPolicy ¶
type RetryPolicy struct {
// MaxAttempts is the total number of attempts (not counting only retries).
MaxAttempts int
// BaseDelay is the first backoff interval.
BaseDelay time.Duration
// MaxDelay caps the exponential backoff interval.
MaxDelay time.Duration
// Multiplier is the exponential growth factor between attempts.
Multiplier float64
// Jitter selects the randomization strategy.
Jitter JitterMode
// PerAttempt optionally bounds each individual attempt.
PerAttempt time.Duration
// Retryable classifies a failed result as transient (retryable) or not.
// When nil, defaultRetryable is used.
Retryable func(res RunResult, err error) bool
}
RetryPolicy controls transient-failure handling for network-dependent gomote commands. The zero value is filled in with the values from DefaultRetryPolicy.
func DefaultRetryPolicy ¶
func DefaultRetryPolicy() RetryPolicy
DefaultRetryPolicy returns the policy applied when Config.Retry is the zero value: four attempts of full-jitter exponential backoff from 500ms to 30s.
type RunOptions ¶
type RunOptions struct {
// Dir is the working directory on the instance (-dir).
Dir string
// Path sets the remote PATH (-path); elements are joined with ",". The
// gomote tokens $PATH and $WORKDIR are passed through verbatim, and the
// special element "EMPTY" runs with no $PATH at all.
Path []string
// Env are environment variables for the command (-e).
Env []EnvVar
// System runs the command in the system context rather than the work
// directory (-system). gomote also implies this when Command is absolute.
System bool
// Debug requests debug information about the command before it runs (-debug).
Debug bool
// Firewall enables the outbound firewall for the instance lifetime (-firewall).
Firewall bool
// BuilderEnv selects an alternate builder to emulate; it must share the same
// underlying host type (-builderenv).
BuilderEnv string
// Until reruns the command until its output matches this regexp (-until).
// Bound it with the context to avoid running forever.
Until string
// Command is the remote program to execute. Required.
Command string
// Args are the remote program's arguments. They may begin with "-".
Args []string
}
RunOptions configures Instance.Run.
type Source ¶
type Source int
Source describes how a Client located its gomote binary.
const ( // SourceUnknown is the zero value and indicates the binary was not resolved. SourceUnknown Source = iota // SourceExplicit indicates Config.BinaryPath was used. SourceExplicit // SourcePATH indicates the binary was found via the PATH environment. SourcePATH // SourceBootstrap indicates the binary was built into the sandbox. SourceBootstrap )