rig

package module
v2.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

Rig

GoDoc Go Report Card codecov FOSSA Status

Rig is a Go library for managing remote hosts over SSH, WinRM or the local machine with one consistent, testable API.

Connect to a host once and you get command execution, a remote filesystem that implements the standard library's io/fs, automatic OS/distro detection, sudo escalation, service control, and package management the same way whether the target is Linux over SSH, Windows over WinRM or the machine you're running on.


Version note. This is rig v2 (github.com/k0sproject/rig/v2), a ground-up redesign. The stable v0.x line with years of production use history lives on the release-0.x branch.

Migrating from v0.x? See docs/MIGRATING-from-v0.x.md.

Install

go get github.com/k0sproject/rig/v2

Quickstart

package main

import (
	"context"
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/protocol/ssh"
)

func main() {
	client, err := rig.NewClient(rig.WithConnection(&ssh.Connection{
		Config: ssh.Config{Address: "10.0.0.5"},
	}))
	if err != nil {
		panic(err)
	}

	ctx := context.Background()
	if err := client.Connect(ctx); err != nil {
		panic(err)
	}
	defer client.Disconnect()

	out, err := client.ExecOutput("uname -a")
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
}

Features

One API for any transport

The same *rig.Client drives the internal golang.org/x/crypto/ssh based client, the system ssh binary (OpenSSH, with connection multiplexing), WinRM, or the pseudo-connection to local machine. Pick a protocol by handing NewClient a connection or for config-driven apps or embed CompositeConfig / ClientWithConfig to your host struct:

client, err := rig.NewClient(rig.WithConnectionFactory(&rig.CompositeConfig{
	SSH: &ssh.Config{Address: "10.0.0.5", User: "root"},
	// or WinRM: &winrm.Config{...}, OpenSSH: &openssh.Config{...}, Localhost: true
}))
type Host struct {
  rig.CompositeConfig `yaml:",inline"`
}
# the same struct, populated from YAML
hosts:
  - ssh:
    address: 10.0.0.5
    user: root
    port: 22
    keyPath: ~/.ssh/id_ed25519

SSH connections honour your ~/.ssh/config's host aliases, IdentityFile, UserKnownHostsFile and a number of other ssh config options. Host-key handling follows OpenSSH conventions and respects and updates ~/.ssh/known_hosts.

The remote filesystem is an fs.FS

client.FS() returns a filesystem that satisfies the standard library io/fs interfaces — so the entire io/fs ecosystem works against a remote host, no adapters required:

fsys := client.FS()

data, err := fs.ReadFile(fsys, "/etc/os-release")        // stdlib io/fs
matches, err := fs.Glob(fsys, "/etc/*.conf")             // stdlib io/fs
err = fs.WalkDir(fsys, "/var/log", func(p string, d fs.DirEntry, err error) error {
	fmt.Println(p)                                       // walking a tree over SSH
	return nil
})

It also exposes a richer, os-like surface — WriteFile, MkdirAll, Rename, Chmod, Stat, OpenFile (honouring os.O_EXCL & friends), plus OS-aware NativePath/ShellQuote so the same code is correct on Windows and Linux targets. The function signatures mimic those found in stdlib. Atomic uploads with checksum verification come built in:

err = remotefs.Upload(client.FS(), "local/binary", "/usr/local/bin/tool",
	remotefs.WithPermissions(0o755))
Sudo is just another client

client.Sudo() returns a clone of the regular runner whose every command is privilege-escalated (sudo, doas or Windows runas, detected automatically):

sudo := client.Sudo()
sudo.Exec("systemctl restart k0s")            // runs as root
err := sudo.FS().WriteFile("/etc/motd", []byte("hello\n"), 0o644)
if err := client.CheckSudo(ctx); err != nil { /* escalation unavailable */ }
Manage the host

OS detection, service control, and package management are lazy-initialized providers. They figure out systemd vs OpenRC, apt vs apk vs chocolatey, and so on:

release, _ := client.OS()                              // *os.Release (id, version, ...)
arch, _ := release.Arch()                              // normalized to GOARCH ("amd64", "arm64", ...)

svc, _ := client.Sudo().Service("k0scontroller")
svc.Enable(ctx); svc.Start(ctx)                        // systemd/openrc/winsvc, abstracted

pm := client.Sudo().PackageManager()
pm.Install(ctx, "curl", "tar")                         // apt/yum/dnf/apk/choco
Stream like os/exec

client.Proc() gives you an os/exec.Cmd-style handle. Wire up stdin/stdout/stderr, then run:

proc := client.Proc("tar -xzf - -C /opt")
proc.Stdin = archiveReader
proc.Stdout = os.Stdout
err := proc.Run(ctx)

Every Exec/ExecOutput has an ExecContext/ExecOutputContext twin that takes a context.Context which you can cancel and the remote command will get aborted.

Testing

rigtest provides mock runners and connections so you can unit-test host logic:

runner := rigtest.NewMockRunner()
runner.AddCommandOutput(rigtest.Equal("hostname"), "node-01\n")

out, _ := runner.ExecOutput("hostname")   // "node-01\n" — no host required

Program command responses, assert on what ran, and capture logs, see docs/TESTING.md.

Extensible by injection

Every provider (filesystem, OS release, package manager, init system, logger) can be swapped at construction with a With* option, so you can pin behaviour, stub a subsystem, or add support for something rig doesn't ship (until you make a PR):

client, _ := rig.NewClient(
	rig.WithConnection(conn),
	rig.WithLogger(slog.New(myHandler)),
	rig.WithOSReleaseProvider(func(cmd.SimpleRunner) (*rigos.Release, error) {
		return &rigos.Release{ID: "alpine", Version: "3.18.0"}, nil
	}),
)

To teach rig about a new package manager, init system, or OS — using the same detection registries rig uses internally — see docs/EXTENDING.md.

A complete ssh_config parser

The sshconfig package is a from-scratch parser for OpenSSH's ssh_config format. It's possibly currently the most complete ssh_config parser available for Go. It implements every field in the ssh_config(5) and supports Match directives, Include (in lexical order, with circular-include detection), expansion of ~, environment variables and TOKENS, the +/-/^ list-modifier prefixes (KexAlgorithms, HostKeyAlgorithms, …), multistate fields, hostname canonicalization, origin-based value precedence, and OpenSSH-faithful unquoting/splitting (argv_split ported from OpenSSH's source). Resolve the effective settings for a host into a struct of your choosing:

cfg, err := sshconfig.ConfigFor("myhost")   // reads ~/.ssh/config + system config, applies Match/Include
fmt.Println(cfg.User, cfg.IdentityFile, cfg.ProxyJump)

rig's native SSH transport consumes it internally, but the package can be imported standalone. See sshconfig/README.md for the full feature list and a detailed comparison with the existing Go alternatives.

Safe shell & PowerShell command construction

The sh package builds POSIX command strings with every argument escaped for you, so you can stop hand-rolling fmt.Sprintf and manual quoting. sh/shellescape is a drop-in replacement for alessio/shellescape and adds Split, Join, Unquote, and Expand:

cmd := sh.Command("grep", "-r", userInput, dir)        // each arg quoted safely
pipe := sh.CommandBuilder("journalctl").Arg("-u").Arg("k0s").
	Pipe("grep", "error").OutToFile("/tmp/err.log")    // chainable pipes & redirects

The powershell package does the equivalent for Windows targets — Cmd, EncodeCmd (base64 -EncodedCommand), CompressedCmd, plus SingleQuote/DoubleQuote and Windows-path helpers:

ps := powershell.EncodeCmd("Get-Service | Where-Object Status -eq Running")
Keep secrets out of logs

The redact package scrubs sensitive values from strings or wraps an io.Reader / io.Writer so credentials never reach your logs or terminal:

w := redact.Writer(os.Stdout, "[REDACTED]", apiToken, password)   // io.WriteCloser
clean := redact.StringRedacter("***", secret).Redact(logLine)

These are the same helpers rig uses internally (e.g. the cmd.Redact exec option is backed by redact).

Built-in Protocols

  • SSH — native Go SSH (golang.org/x/crypto/ssh) with ssh-agent and ssh_config support and sane defaults. Pageant / OpenSSH agent work on Windows.
  • OpenSSH — drives the system's own ssh binary, reusing connections via session multiplexing for speed and full ssh_config fidelity (ProxyJump, GSSAPI, …).
  • WinRM — for Windows hosts (SSH works on Windows too and is becoming the default for most installations).
  • Localhost — treat the local machine the same as a remote host via os/exec.

Documentation

Projects using rig

  • k0sctl - a k0s cluster bootstrapping, deployment and management tool.
  • k0smotron - run Kubernetes control planes within a management cluster and with the integration of Cluster API
  • launchpad - automate the installation, upgrading, and resetting of MKE (Mirantis Kubernetes Engine) and MCR (Mirantis Container Runtime) clusters on provisioned compute node resources

License

FOSSA Status

Documentation

Overview

Package rig provides an easy way to add multi-protocol connectivity and multi-os operation support to your application's Host objects by embedding or directly using the Client or Connection objects.

Rig's core functionality revolves around providing a unified interface for interacting with remote systems. This includes managing services, file systems, package managers, and getting OS release information, abstracting away the intricacies of different operating systems and communication protocols.

The protocol implementations aim to provide out-of-the-box default behavior similar to what you would expect when using the official clients like openssh "ssh" command instead of having to deal with implementing ssh config parsing, key managemnt, agent forwarding and so on yourself.

To get started, see Client

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNonRetryable is returned when retrying an action will not yield a different outcome.
	// An alias of protocol.ErrNonRetryable for easier access without importing subpackages.
	ErrNonRetryable = protocol.ErrNonRetryable

	// ErrValidationFailed is returned when a validation check fails.
	// An alias of protocol.ErrValidationFailed for easier access without importing subpackages.
	ErrValidationFailed = protocol.ErrValidationFailed
)

Functions

func DefaultPasswordCallback

func DefaultPasswordCallback() (string, error)

DefaultPasswordCallback is a default implementation for PasswordCallback.

func GetOSRelease

func GetOSRelease(runner cmd.SimpleRunner) (*os.Release, error)

GetOSRelease returns the remote host's operating system information from the default OS release registry.

func GetPackageManager

func GetPackageManager(runner cmd.ContextRunner) (packagemanager.PackageManager, error)

GetPackageManager returns a package manager from the default package manager registry.

func GetRemoteFS

func GetRemoteFS(runner cmd.Runner) (remotefs.FS, error)

GetRemoteFS returns a new remote FS instance from the default remote FS registry.

func GetServiceManager

func GetServiceManager(runner cmd.ContextRunner) (initsystem.ServiceManager, error)

GetServiceManager returns a ServiceManager for the current system from the default init system providers.

func GetSudoRunner

func GetSudoRunner(runner cmd.Runner) (cmd.Runner, error)

GetSudoRunner returns a new runner that uses sudo to execute commands.

func NewRunner

func NewRunner(conn protocol.Connection) (cmd.Runner, error)

NewRunner returns a new cmd.Runner for the connection. Currently the error is always nil.

Types

type Capabilities

type Capabilities struct {
	// Protocol is the connection protocol implementation name (e.g. "SSH", "OpenSSH", "WinRM", "Local").
	// OpenSSH and native SSH connections return distinct values ("OpenSSH" vs "SSH").
	// Free: derived from the connection without a remote round-trip.
	Protocol string

	// OS is the detected operating system release information.
	// Populated on the first call that resolves OS info; nil if detection failed.
	OS *os.Release
	// OSErr is the OS detection error, if any.
	OSErr error

	// RemoteFS indicates whether the remote filesystem is available via [Client.FS].
	RemoteFS bool
	// RemoteFSErr is the remote filesystem initialization error, if any.
	RemoteFSErr error

	// PackageManager is the name of the detected package manager (e.g. "apt", "yum",
	// "dnf"). Empty if no package manager was detected.
	PackageManager string
	// PackageManagerErr is the package manager detection error, if any.
	PackageManagerErr error

	// InitSystem is the name of the detected init system (e.g. "systemd", "openrc").
	// Empty if no init system was detected.
	InitSystem string
	// InitSystemErr is the init system detection error, if any.
	InitSystemErr error

	// Sudo indicates whether privileged command execution is available via [Client.Sudo].
	// This is true both when already running as root/administrator (no escalation needed)
	// and when a supported escalation mechanism (sudo, doas) was detected. False only when
	// [Client.Sudo] would return an error (e.g. no sudo binary found and not root).
	Sudo bool
	// SudoErr is the sudo detection error, if any.
	SudoErr error

	// InteractiveExec indicates whether the underlying connection supports interactive
	// command execution via [Client.ExecInteractive].
	// Free: determined by a local interface check with no remote round-trip.
	InteractiveExec bool
}

Capabilities summarizes what was detected about the remote host. All detection results are memoized: the first call to Capabilities (or to any of the individual accessor methods such as OS, FS, PackageManager, etc.) that triggers a probe runs a remote command; every subsequent call returns the cached result without additional network round-trips.

func (Capabilities) String

func (c Capabilities) String() string

String returns a human-readable summary of the detected capabilities.

type Client

type Client struct {
	cmd.Runner

	log.LoggerInjectable

	*PackageManagerProvider
	*InitSystemProvider
	*RemoteFSProvider
	*OSReleaseProvider
	*SudoProvider
	// contains filtered or unexported fields
}

Client is a swiss army knife client that can perform actions and run commands on target hosts running on multiple operating systems and using different protocols for communication.

It provides a consistent interface to the host's init system, package manager, file system, and more, regardless of the protocol or the remote operating system. It also provides a consistent interface to the host's operating system's basic functions in a similar manner as the stdlib's os package does for the local system, for example chmod, stat, and so on.

The easiest way to set up a client instance is through a protocol config struct, like protocol/ssh.Config or the unified CompositeConfig and then use the NewClient function to create a new client.

func NewClient

func NewClient(opts ...ClientOption) (*Client, error)

NewClient returns a new Connection object with the given options.

You must use either WithConnection to provide a pre-configured connection or WithConnectionFactory to provide a connection factory.

An example SSH connection via ssh.Config:

client, err := rig.NewClient(WithConnectionFactory(&ssh.Config{Address: "10.0.0.1"}))

Using the CompositeConfig struct:

client, err := rig.NewClient(WithConnectionFactory(&rig.CompositeConfig{SSH: &ssh.Config{...}}))

If you want to use a pre-configured connection, you can use WithConnection:

conn, err := ssh.NewConnection(ssh.Config{...})
client, err := rig.NewClient(WithConnection(conn))

Once you have a client, you can use it to interact with the remote host.

err := client.Connect(context.Background())
if err != nil {
    log.Fatal(err)
}
out, err := client.ExecOutput("ls")

To see all of the available ways to run commands, see cmd.Executor.

Example (Localhost)

ExampleNewClient_localhost demonstrates connecting to the local machine via the localhost protocol and running a command.

package main

import (
	"context"
	"fmt"

	rig "github.com/k0sproject/rig/v2"
)

func main() {
	client, err := rig.NewClient(
		rig.WithConnectionFactory(&rig.CompositeConfig{Localhost: true}),
	)
	if err != nil {
		fmt.Println("create client:", err)
		return
	}
	if err := client.Connect(context.Background()); err != nil {
		fmt.Println("connect:", err)
		return
	}
	defer client.Disconnect()

	out, err := client.ExecOutput("echo hello")
	if err != nil {
		fmt.Println("exec:", err)
		return
	}
	fmt.Println(out)
}
Output:
hello

func (*Client) Address

func (c *Client) Address() string

Address returns the address of the host.

func (*Client) Capabilities

func (c *Client) Capabilities() Capabilities

Capabilities probes all detectable aspects of the remote host and returns a summary. Aspects that have already been probed (for example by a prior call to Client.OS, Client.FS, Client.PackageManager, or Client.Sudo) return their cached result without issuing additional remote commands.

Some capabilities may run remote commands on first access (OS, PackageManager, InitSystem, and Sudo); others are resolved locally without network round-trips (Protocol, RemoteFS, and InteractiveExec). All results are memoized so repeated calls or interleaved direct accessor calls remain cheap.

func (*Client) CheckSudo

func (c *Client) CheckSudo(_ context.Context) error

CheckSudo eagerly validates that privileged command execution is available on the remote host. It returns nil if Client.Sudo would succeed, or a wrapped error describing why privilege escalation is unavailable.

A context is accepted for API consistency and to allow future implementations to perform an active liveness check, but is not used today.

func (*Client) Clone

func (c *Client) Clone(opts ...ClientOption) *Client

Clone returns a copy of the connection with the given additional options applied.

func (*Client) Connect

func (c *Client) Connect(ctx context.Context) error

Connect to the host. The connection is attempted until the context is done or the protocol implementation returns an error indicating that the connection can't be established by retrying. If a context without a deadline is used, a 10 second timeout is used.

func (*Client) Disconnect

func (c *Client) Disconnect()

Disconnect from the host.

func (*Client) ExecInteractive

func (c *Client) ExecInteractive(ctx context.Context, command string, stdin io.Reader, stdout, stderr io.Writer) error

ExecInteractive executes a command on the host and passes stdin/stdout/stderr as-is to the session. The session is terminated when ctx is cancelled or its deadline is exceeded.

A configured cmd.CommandGate is consulted for the command before the session starts. Because interactive exec runs directly on the connection rather than through the runner, the gate sees the raw command as given here, without sudo/shell decoration or secret redaction, and commands typed inside the interactive session are not gated.

func (*Client) FS

func (c *Client) FS() remotefs.FS

FS returns an fs.FS compatible filesystem interface for accessing files on the host.

If the filesystem can't be accessed, a filesystem that returns an error for all operations is returned instead. If you need to handle the error, you can use c.RemoteFSProvider.FS() directly.

Example

ExampleClient_FS demonstrates using Client.FS to read a file from the remote host via the fs.FS interface.

package main

import (
	"fmt"
	"strings"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()
	// PosixFS reads file content by running a cat-like command.
	conn.AddCommandOutput(rigtest.Contains("cat"), "node-01\n")

	client, err := rig.NewClient(rig.WithConnection(conn))
	if err != nil {
		fmt.Println(err)
		return
	}

	data, err := client.FS().ReadFile("etc/hostname")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(strings.TrimSpace(string(data)))
}
Output:
node-01

func (*Client) IsConnected

func (c *Client) IsConnected() bool

IsConnected returns true if the underlying connection is currently active. This delegates to the protocol connection's IsConnected, which may perform an active liveness probe (e.g. ssh -O check for OpenSSH multiplexed sessions, or a no-op command for WinRM/non-multiplexed SSH) and may block up to a timeout.

func (*Client) OS

func (c *Client) OS() (*os.Release, error)

OS returns the host's operating system version and release information or an error if it can't be determined.

func (*Client) PackageManager

func (c *Client) PackageManager() packagemanager.PackageManager

PackageManager for the host's operating system. This can be used to install or remove packages.

If a known package manager can't be detected, a PackageManager that returns an error for all operations is returned. If you need to handle the error, you can use client.PackageManagerProvider.PackageManager() directly.

func (*Client) Protocol

func (c *Client) Protocol() string

Protocol returns the protocol family used to connect to the host, such as "SSH", "WinRM", or "Local". Both the native SSH and OpenSSH implementations return "SSH". Custom or test implementations may return other values.

func (*Client) ProtocolName

func (c *Client) ProtocolName() string

ProtocolName returns the specific protocol implementation name, such as "SSH", "OpenSSH", "WinRM", or "Local". Use this for logging or diagnostics where the distinction between native SSH and OpenSSH matters. Custom or test implementations may return other values.

func (*Client) Reboot

func (c *Client) Reboot(ctx context.Context) error

Reboot triggers an immediate restart of the remote host. The method returns as soon as the reboot has been requested; the caller is responsible for polling Client.IsConnected until the host goes down and comes back.

Callers that need elevated privileges should invoke this on a sudo-decorated client (for example c.Sudo().Reboot(ctx)).

func (*Client) Service

func (c *Client) Service(name string) (*Service, error)

Service returns a manager for a named service on the remote host using the host's init system if one can be detected. This can be used to start, stop, restart, and check the status of services.

You most likely need to use this with Sudo:

service, err := client.Sudo().Service("nginx")

func (*Client) String

func (c *Client) String() string

String returns a printable representation of the connection, which will usually look something like: `address:port` or `user@address:port`.

func (*Client) Sudo

func (c *Client) Sudo() *Client

Sudo returns a copy of the connection with a Runner that uses sudo.

Example

ExampleClient_Sudo demonstrates obtaining a sudo-decorated client and using it to run a privileged command.

package main

import (
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()
	// Accept the sudo probe ("sudo -n -- ... true").
	conn.AddCommand(rigtest.HasSuffix("true'"), func(_ *rigtest.A) error { return nil })
	// The actual command runs wrapped in sudo.
	conn.AddCommand(rigtest.Contains("id"), func(a *rigtest.A) error {
		fmt.Fprintln(a.Stdout, "uid=0(root)")
		return nil
	})

	client, err := rig.NewClient(rig.WithConnection(conn))
	if err != nil {
		fmt.Println(err)
		return
	}

	out, err := client.Sudo().ExecOutput("id")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(out)
}
Output:
uid=0(root)

type ClientOption

type ClientOption func(*ClientOptions)

ClientOption is a functional option type for the Options struct.

func WithCommandGate added in v2.1.0

func WithCommandGate(gate cmd.CommandGate) ClientOption

WithCommandGate installs a cmd.CommandGate that is consulted before commands run on the client and all of its derived runners (sudo, filesystem, services). Returning a non-nil error from the gate aborts the command, wrapped in cmd.ErrCommandRejected; a context cancellation/deadline error is the exception and propagates as a context error without cmd.ErrCommandRejected. Use WithConfirmFunc for the common case of a yes/no confirmation prompt. See cmd.CommandGate for exactly which commands are gated and which internal paths are not.

The gate reaches a runner only if that runner implements cmd.CommandGateSetter. The default runner does; a custom runner supplied via WithRunner that does not implement it runs ungated, and the client logs a warning during setup.

func WithConfirmFunc added in v2.1.0

func WithConfirmFunc(fn func(host, command string) bool) ClientOption

WithConfirmFunc installs a confirmation callback consulted before commands run on the client and all of its derived runners (sudo, filesystem, services). The callback receives the host string and the human-readable, redacted command; returning false aborts the command with cmd.ErrCommandRejected. This is a convenience wrapper around WithCommandGate; see cmd.CommandGate for which commands are gated. A nil fn disables gating, matching WithCommandGate(nil).

One exception: Client.ExecInteractive runs directly on the connection, so its command reaches the callback raw — undecorated and unredacted. Avoid logging the callback argument as-is if interactive commands may carry secrets.

Example

ExampleWithConfirmFunc demonstrates gating every command behind a confirmation callback. The callback receives the host and the redacted command; returning false aborts the command with cmd.ErrCommandRejected before it reaches the host. This applies to the client and every runner it derives (sudo, filesystem, services).

package main

import (
	"errors"
	"fmt"
	"strings"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/cmd"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()
	conn.AddCommand(rigtest.Match("."), func(_ *rigtest.A) error { return nil })

	client, err := rig.NewClient(
		rig.WithConnection(conn),
		rig.WithConfirmFunc(func(_, command string) bool {
			allowed := !strings.Contains(command, "rm -rf")
			fmt.Printf("confirm %q -> %v\n", command, allowed)
			return allowed
		}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	// An approved command runs normally.
	if err := client.Exec("systemctl restart nginx"); err != nil {
		fmt.Println("restart:", err)
	}

	// A rejected command never reaches the host.
	if err := client.Exec("rm -rf /important"); errors.Is(err, cmd.ErrCommandRejected) {
		fmt.Println("blocked")
	}
}
Output:
confirm "systemctl restart nginx" -> true
confirm "rm -rf /important" -> false
blocked

func WithConnection

func WithConnection(conn protocol.Connection) ClientOption

WithConnection is a functional option that sets the client to use for connecting instead of getting it from the ConnectionFactory.

func WithConnectionFactory

func WithConnectionFactory(factory ConnectionFactory) ClientOption

WithConnectionFactory is a functional option that sets the connection factory to use for connecting.

func WithInitSystemProvider

func WithInitSystemProvider(provider initsystem.ServiceManagerProvider) ClientOption

WithInitSystemProvider is a functional option that sets the init system provider to use for the connection's InitSystemProvider.

Example

ExampleWithInitSystemProvider demonstrates injecting a custom init system provider so that Client.ServiceManager returns a known result.

package main

import (
	"errors"
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/cmd"
	"github.com/k0sproject/rig/v2/initsystem"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()

	client, err := rig.NewClient(
		rig.WithConnection(conn),
		rig.WithInitSystemProvider(func(_ cmd.ContextRunner) (initsystem.ServiceManager, error) {
			return nil, errors.New("no init system detected")
		}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	if _, err := client.ServiceManager(); err != nil {
		fmt.Println("service manager:", err)
	}
}
Output:
service manager: get service manager: no init system detected

func WithLogger

func WithLogger(logger log.Logger) ClientOption

WithLogger is a functional option that sets the logger to use for the connection and its child components.

func WithOSIDOverride

func WithOSIDOverride(id string) ClientOption

WithOSIDOverride is a functional option that overrides the OS ID reported by the host's detected os.Release. Detection still runs normally; only the ID field of the result is replaced. IDLike from actual detection is preserved so callers can still inspect the distro family. This is useful when the detected ID does not match any known configurer but a compatible one is known (e.g. an unsupported derivative of a supported distro).

func WithOSReleaseProvider

func WithOSReleaseProvider(provider os.ReleaseProvider) ClientOption

WithOSReleaseProvider is a functional option that sets the os release provider to use for the connection's OSReleaseProvider.

Example

ExampleWithOSReleaseProvider demonstrates injecting a custom OS release provider that bypasses remote detection.

package main

import (
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/cmd"

	rigos "github.com/k0sproject/rig/v2/os"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()

	client, err := rig.NewClient(
		rig.WithConnection(conn),
		rig.WithOSReleaseProvider(func(_ cmd.SimpleRunner) (*rigos.Release, error) {
			return &rigos.Release{ID: "alpine", Name: "Alpine Linux", Version: "3.18.0"}, nil
		}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	release, err := client.OS()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%s %s\n", release.ID, release.Version)
}
Output:
alpine 3.18.0

func WithPackageManagerProvider

func WithPackageManagerProvider(provider packagemanager.ManagerProvider) ClientOption

WithPackageManagerProvider is a functional option that sets the package manager provider to use for the connection's PackageManagerProvider.

Example

ExampleWithPackageManagerProvider demonstrates injecting a custom package manager so that Client.PackageManager returns a known implementation.

package main

import (
	"context"
	"errors"
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/cmd"
	"github.com/k0sproject/rig/v2/packagemanager"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()

	client, err := rig.NewClient(
		rig.WithConnection(conn),
		rig.WithPackageManagerProvider(func(_ cmd.ContextRunner) (packagemanager.PackageManager, error) {
			return &packagemanager.NullPackageManager{Err: errors.New("no package manager")}, nil
		}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	pm := client.PackageManager()
	if err := pm.Install(context.Background(), "curl"); err != nil {
		fmt.Println("install:", err)
	}
}
Output:
install: install packages (curl): no package manager

func WithRemoteFSProvider

func WithRemoteFSProvider(provider remotefs.FSProvider) ClientOption

WithRemoteFSProvider is a functional option that sets the filesystem provider to use for the connection's RemoteFSProvider.

Example

ExampleWithRemoteFSProvider demonstrates injecting a custom filesystem provider so that Client.FS returns a specific implementation.

package main

import (
	"errors"
	"fmt"

	rig "github.com/k0sproject/rig/v2"
	"github.com/k0sproject/rig/v2/cmd"
	"github.com/k0sproject/rig/v2/remotefs"
	"github.com/k0sproject/rig/v2/rigtest"
)

func main() {
	conn := rigtest.NewMockConnection()

	client, err := rig.NewClient(
		rig.WithConnection(conn),
		rig.WithRemoteFSProvider(func(_ cmd.Runner) (remotefs.FS, error) {
			return nil, errors.New("filesystem access disabled")
		}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	_, err = client.RemoteFSProvider.FS()
	if err != nil {
		fmt.Println("fs:", err)
	}
}
Output:
fs: get filesystem: filesystem access disabled

func WithRetry

func WithRetry(retry bool) ClientOption

WithRetry is a functional option that toggles the connection retry feature. Default is true.

func WithRunner

func WithRunner(runner cmd.Runner) ClientOption

WithRunner is a functional option that sets the runner to use for executing commands.

func WithSudoProvider

func WithSudoProvider(provider sudo.RunnerProvider) ClientOption

WithSudoProvider is a functional option that sets the sudo provider to use for the connection's SudoProvider.

type ClientOptions

type ClientOptions struct {
	log.LoggerInjectable
	// contains filtered or unexported fields
}

ClientOptions is a struct that holds the variadic options for the rig package.

func DefaultClientOptions

func DefaultClientOptions() *ClientOptions

DefaultClientOptions returns a new Options struct with the default options applied.

func NewClientOptions

func NewClientOptions(opts ...ClientOption) *ClientOptions

NewClientOptions creates a new Options struct with the supplied options applied over the defaults.

func (*ClientOptions) Apply

func (o *ClientOptions) Apply(opts ...ClientOption)

Apply applies the supplied options to the Options struct.

func (*ClientOptions) Clone

func (o *ClientOptions) Clone() *ClientOptions

Clone returns a copy of the Options struct.

func (*ClientOptions) GetConnection

func (o *ClientOptions) GetConnection() (protocol.Connection, error)

GetConnection returns the connection to use for the rig client. If no connection is set, it will use the ConnectionFactory to create one.

func (*ClientOptions) GetRunner

func (o *ClientOptions) GetRunner(conn protocol.Connection) cmd.Runner

GetRunner returns the runner to use for the rig client.

func (*ClientOptions) ShouldRetry

func (o *ClientOptions) ShouldRetry() bool

ShouldRetry returns whether the connection should be retried.

func (*ClientOptions) Validate

func (o *ClientOptions) Validate() error

Validate the options.

type ClientWithConfig

type ClientWithConfig struct {
	ConnectionConfig CompositeConfig `yaml:",inline"`
	*Client          `yaml:"-"`
	// contains filtered or unexported fields
}

ClientWithConfig is a Client that is suitable for embedding into a host object that is unmarshalled from YAML configuration.

When embedded into a "host" object like this:

type Host struct {
  rig.ClientWithConfig `yaml:",inline"`
  // ...
}

And having a configuration YAML like this:

hosts:
- ssh:
  address: 10.0.0.1
  user: root

You can unmarshal the configuration and start using the clients on the host objects:

if err := host.Connect(context.Background()); err != nil {
    log.Fatal(err)
}
out, err := host.ExecOutput("ls")

The available protocols are defined in the CompositeConfig struct.

func (*ClientWithConfig) Connect

func (c *ClientWithConfig) Connect(ctx context.Context, opts ...ClientOption) error

Connect to the host. Unlike in Client.Connect, the Connect method here accepts a variadic list of options similar to NewClient. This is to allow configuring the connection before connecting, since you won't be calling NewClient to create the ClientWithConfig instance when unmarshalling from a configuration file.

func (*ClientWithConfig) Setup

func (c *ClientWithConfig) Setup(opts ...ClientOption) error

Setup allows applying options to the connection to configure subcomponents.

func (*ClientWithConfig) UnmarshalYAML

func (c *ClientWithConfig) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML implements the yaml.Unmarshaler interface. It unmarshals the connection configuration but defers client setup to ClientWithConfig.Connect or an explicit ClientWithConfig.Setup call, so that options (logger, retry policy, etc.) passed at connect time are not silently ignored. If an existing client is present (e.g. when unmarshaling into a previously connected instance), it is disconnected and the client is reset so that the new configuration takes effect on the next Connect or Setup call.

type CompositeConfig

type CompositeConfig struct {
	SSH       *ssh.Config     `yaml:"ssh,omitempty"`
	WinRM     *winrm.Config   `yaml:"winRM,omitempty"`
	OpenSSH   *openssh.Config `yaml:"openSSH,omitempty"`
	Localhost LocalhostConfig `yaml:"localhost,omitempty"`
}

CompositeConfig is a composite configuration of all the protocols supported out of the box by rig. It is intended to be embedded into host structs that are unmarshaled from configuration files.

func (*CompositeConfig) Connection

func (c *CompositeConfig) Connection() (protocol.Connection, error)

Connection returns a connection for the first configured protocol.

func (*CompositeConfig) String

func (c *CompositeConfig) String() string

String returns the string representation of the first configured protocol configuration.

func (*CompositeConfig) Validate

func (c *CompositeConfig) Validate() error

Validate the configuration.

type ConnectionFactory

type ConnectionFactory interface {
	fmt.Stringer
	Connection() (protocol.Connection, error)
}

ConnectionFactory can create connections. When a connection is not given, the factory is used to build a connection.

type InitSystemProvider

type InitSystemProvider = initsystem.Provider

InitSystemProvider is a type alias for initsystem.Provider.

type LocalhostConfig

type LocalhostConfig bool

LocalhostConfig is a bool-valued type that also accepts the v0.x YAML form "localhost:\n enabled: true" for backward compatibility. To assign a bool variable, use an explicit cast: LocalhostConfig(b).

func (*LocalhostConfig) UnmarshalYAML

func (l *LocalhostConfig) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML implements yaml.Unmarshaler, accepting both:

localhost: true           (current form)
localhost:                (v0.x form)
  enabled: true

type OSReleaseProvider

type OSReleaseProvider = os.Provider

OSReleaseProvider is a type alias for os.Provider.

type PackageManagerProvider

type PackageManagerProvider = packagemanager.Provider

PackageManagerProvider is a type alias for packagemanager.Provider.

type RemoteFSProvider

type RemoteFSProvider = remotefs.Provider

RemoteFSProvider is a type alias for remotefs.Provider.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service running on a host.

func GetService

func GetService(runner cmd.ContextRunner, name string) (*Service, error)

GetService returns a manager for a single service using an auto-detected service manager implementation from the default providers.

func (*Service) Disable

func (m *Service) Disable(ctx context.Context) error

Disable the service. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) Enable

func (m *Service) Enable(ctx context.Context) error

Enable the service. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) IsRunning

func (m *Service) IsRunning(ctx context.Context) bool

IsRunning returns true if the service is running. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) Logs

func (m *Service) Logs(ctx context.Context, lines int) ([]string, error)

Logs returns latest log lines for the service. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) Name

func (m *Service) Name() string

Name returns the name of the service.

func (*Service) Restart

func (m *Service) Restart(ctx context.Context) error

Restart the service. For init systems with a native restart operation, a 2-minute default timeout is applied if ctx has no deadline. For the stop+start fallback, each step applies its own independent default timeout so the combined operation can take up to 4 minutes.

func (*Service) ScriptPath

func (m *Service) ScriptPath(ctx context.Context) (string, error)

ScriptPath returns the path to the service script. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) SetEnvironment

func (m *Service) SetEnvironment(ctx context.Context, env map[string]string) error

SetEnvironment writes environment variable overrides for the service and triggers a daemon-reload if the init system requires it (e.g. systemd). The init system determines the file path and format; any existing content is replaced. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) Start

func (m *Service) Start(ctx context.Context) error

Start the service. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) Stop

func (m *Service) Stop(ctx context.Context) error

Stop the service. If ctx has no deadline, a 2-minute default timeout is applied.

func (*Service) StreamLogs

func (m *Service) StreamLogs(ctx context.Context, w io.Writer) error

StreamLogs streams new service log output to w from the current point in time until ctx is cancelled or an error occurs. Cancelling ctx is the expected way to stop streaming and does not return an error.

func (*Service) String

func (m *Service) String() string

String returns a string representation of the service.

type SudoProvider

type SudoProvider = sudo.Provider

SudoProvider is a type alias for sudo.Provider.

Directories

Path Synopsis
Package byteslice contains functions for working with byte slices.
Package byteslice contains functions for working with byte slices.
Package cmd defines types and functions for running commands.
Package cmd defines types and functions for running commands.
Package homedir provides functions for expanding paths like ~/.ssh.
Package homedir provides functions for expanding paths like ~/.ssh.
Package initsystem provides a common interface for interacting with init systems like systemd, openrc, sysvinit, etc.
Package initsystem provides a common interface for interacting with init systems like systemd, openrc, sysvinit, etc.
Package iostream contains various io.Reader and io.Writer implementations.
Package iostream contains various io.Reader and io.Writer implementations.
Package kv is for working with key-value pair files and strings often found in configuration files, environment variables, and other sources.
Package kv is for working with key-value pair files and strings often found in configuration files, environment variables, and other sources.
Package log contains rig's logging related types, constants and functions.
Package log contains rig's logging related types, constants and functions.
Package os provides remote OS release information detection
Package os provides remote OS release information detection
Package packagemanager provides a generic interface for package managers.
Package packagemanager provides a generic interface for package managers.
Package plumbing defines generic types for the dependency injection mechanisms in rig.
Package plumbing defines generic types for the dependency injection mechanisms in rig.
Package powershell provides helpers for powershell command generation
Package powershell provides helpers for powershell command generation
Package protocol contains the interfaces for the protocol implementations
Package protocol contains the interfaces for the protocol implementations
localhost
Package localhost provides a rig protocol implementation to the local host using the os/exec package.
Package localhost provides a rig protocol implementation to the local host using the os/exec package.
openssh
Package openssh provides a rig protocol implementation that uses the system's openssh client "ssh" to connect to remote hosts.
Package openssh provides a rig protocol implementation that uses the system's openssh client "ssh" to connect to remote hosts.
ssh
Package ssh provides a rig protocol implementation for SSH connections.
Package ssh provides a rig protocol implementation for SSH connections.
ssh/agent
Package agent provides a client implementation for the SSH agent.
Package agent provides a client implementation for the SSH agent.
ssh/hostkey
Package hostkey implements a callback for the ssh.ClientConfig.HostKeyCallback
Package hostkey implements a callback for the ssh.ClientConfig.HostKeyCallback
winrm
Package winrm provides a rig protocol implementation for WinRM connections
Package winrm provides a rig protocol implementation for WinRM connections
Package redact provides redaction of sensitive information from strings or streams.
Package redact provides redaction of sensitive information from strings or streams.
Package remotefs provides fs.FS implementations for remote filesystems.
Package remotefs provides fs.FS implementations for remote filesystems.
Package retry provides context based retry functionality for functions.
Package retry provides context based retry functionality for functions.
Package rigtest provides testing utilities for mocking functionality of the rig package.
Package rigtest provides testing utilities for mocking functionality of the rig package.
sh
Package sh provides tools to build and manipulate shell commands.
Package sh provides tools to build and manipulate shell commands.
shellescape
Package shellescape provides functions to escape strings for use in posix shell commands.
Package shellescape provides functions to escape strings for use in posix shell commands.
Package sshconfig provides a parser for OpenSSH ssh_config files as documented in the [man page].
Package sshconfig provides a parser for OpenSSH ssh_config files as documented in the [man page].
options
Package options defines a number of types that represent options that can be set in the SSH configuration file.
Package options defines a number of types that represent options that can be set in the SSH configuration file.
Package stattime provides time comparison functions that work with times up to the finest common precision.
Package stattime provides time comparison functions that work with times up to the finest common precision.
Package sudo provides support for various methods of running commands with elevated privileges.
Package sudo provides support for various methods of running commands with elevated privileges.

Jump to

Keyboard shortcuts

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