agent

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package agent is stampede agent: a helper that runs inside the user's environment and injects faults into dependencies during a load test.

It places TCP proxies in front of dependencies (a database, a cache, a downstream API) and, on request, adds latency and jitter, limits bandwidth, resets connections, refuses new ones or stops forwarding. With explicit flags it can also pause, stop or restart Docker containers and scale Kubernetes deployments. Every fault has a duration and is reverted when it ends, when it is cleared (the kill switch), or when the agent stops. Every action is written to an audit log.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotAllowed = errors.New("not allowed")

ErrNotAllowed means the agent was not started with permission for the action.

Functions

This section is empty.

Types

type Active

type Active struct {
	ID      string    `json:"id"`
	Request Request   `json:"request"`
	Started time.Time `json:"started"`
	Ends    time.Time `json:"ends"`
	// contains filtered or unexported fields
}

Active is a fault in force.

type Agent

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

Agent applies and reverts faults.

func New

func New(cfg Config) *Agent

New returns an agent.

func (*Agent) Active

func (a *Agent) Active() []Active

Active lists faults in force, oldest first.

func (*Agent) Apply

func (a *Agent) Apply(ctx context.Context, req Request) (*Active, error)

Apply starts a fault. It replaces any fault already active on the same target, and reverts by itself after the duration.

func (*Agent) Handler

func (a *Agent) Handler(token string) http.Handler

Handler serves the control API. Every request needs "Authorization: Bearer <token>".

GET    /v1/status
POST   /v1/faults         a Request; answers the Active fault
DELETE /v1/faults/{id}    revert one fault
DELETE /v1/faults?run=ID  revert every fault (of one run): the kill switch

func (*Agent) Proxies

func (a *Agent) Proxies() []ProxyInfo

Proxies describes every proxy.

func (*Agent) Revert

func (a *Agent) Revert(ctx context.Context, id, why string) error

Revert ends one fault.

func (*Agent) RevertAll

func (a *Agent) RevertAll(ctx context.Context, run, why string) error

RevertAll ends every fault, or only those of one run when run is set. It is the kill switch.

type Client

type Client struct {
	BaseURL string
	Token   string
	HTTP    *http.Client
}

Client talks to an agent's control API.

func (*Client) Apply

func (c *Client) Apply(ctx context.Context, req Request) (*Active, error)

Apply starts a fault.

func (*Client) Revert

func (c *Client) Revert(ctx context.Context, id string) error

Revert ends one fault.

func (*Client) RevertAll

func (c *Client) RevertAll(ctx context.Context, run string) error

RevertAll ends every fault of a run (all faults when run is empty).

func (*Client) Status

func (c *Client) Status(ctx context.Context) (*Status, error)

Status reads the agent's state.

type Config

type Config struct {
	Proxies []*Proxy
	// Docker, when set, allows container actions.
	Docker *Docker
	// Kubernetes, when set, allows deployment scaling.
	Kubernetes *Kubernetes
	// MaxDuration caps every fault (default 30 minutes), so a forgotten
	// fault always ends.
	MaxDuration time.Duration
	// Audit receives one record per action and revert.
	Audit  *slog.Logger
	Logger *slog.Logger
}

Config configures an Agent.

type Docker

type Docker struct {
	// Allowed lists container names (globs) the agent may act on.
	Allowed []string
	// contains filtered or unexported fields
}

Docker acts on containers through the Docker Engine API.

func NewDocker

func NewDocker(host string, allowedNames []string) (*Docker, error)

NewDocker talks to the Engine API at host: unix:///var/run/docker.sock (the default), or an http:// URL.

func (*Docker) Act

func (d *Docker) Act(ctx context.Context, name, action string) (func(context.Context) error, error)

Act pauses, stops, kills or restarts a container and returns how to undo it: unpause, or start it again. A restart needs no undo.

type Fault

type Fault struct {
	// Latency is added to each direction of every connection, so a
	// request and its response together take twice Latency longer.
	Latency time.Duration `json:"latency,omitempty"`
	// Jitter adds a random delay in [0, Jitter) on top of Latency.
	Jitter time.Duration `json:"jitter,omitempty"`
	// Bandwidth caps each direction of each connection, in bytes per
	// second (0 = unlimited).
	Bandwidth int64 `json:"bandwidth,omitempty"`
	// Reset resets every open connection when the fault starts, and every
	// new connection while it lasts.
	Reset bool `json:"reset,omitempty"`
	// Refuse resets new connections as soon as they arrive; open ones are
	// left alone.
	Refuse bool `json:"refuse,omitempty"`
	// Blackhole stops forwarding in both directions: connections stay
	// open and requests time out.
	Blackhole bool `json:"blackhole,omitempty"`
}

Fault is what a proxy does to traffic while the fault is active. The zero Fault forwards traffic untouched.

func (Fault) IsZero

func (f Fault) IsZero() bool

IsZero reports whether f changes nothing.

func (Fault) MarshalJSON

func (f Fault) MarshalJSON() ([]byte, error)

MarshalJSON writes durations as strings.

func (*Fault) UnmarshalJSON

func (f *Fault) UnmarshalJSON(b []byte) error

UnmarshalJSON reads durations as strings.

type Kind

type Kind string

Kind is what an action does.

const (
	KindProxy      Kind = "proxy"      // a Fault on a proxy
	KindContainer  Kind = "container"  // pause, stop or restart a Docker container
	KindDeployment Kind = "deployment" // scale a Kubernetes deployment
)

type Kubernetes

type Kubernetes struct {
	// Allowed lists deployments (namespace/name globs) the agent may scale.
	Allowed []string
	// contains filtered or unexported fields
}

Kubernetes scales deployments through the API server, with the pod's service account when running in a cluster.

func NewKubernetes

func NewKubernetes(base, token string, allowedDeployments []string) (*Kubernetes, error)

NewKubernetes uses the in-cluster service account, or base and token when given (tests, or a kubectl proxy at http://127.0.0.1:8001).

func (*Kubernetes) Scale

func (k *Kubernetes) Scale(ctx context.Context, target string, replicas int) (func(context.Context) error, error)

Scale sets a deployment's replicas and returns how to restore them.

type Proxy

type Proxy struct {
	Name     string
	Listen   string
	Upstream string
	// contains filtered or unexported fields
}

Proxy forwards TCP connections from Listen to Upstream, applying the current fault.

func NewProxy

func NewProxy(name, listen, upstream string, log *slog.Logger) *Proxy

NewProxy returns a proxy that is not yet listening.

func (*Proxy) Addr

func (p *Proxy) Addr() string

Addr is the listening address.

func (*Proxy) Close

func (p *Proxy) Close() error

Close stops listening and closes every connection.

func (*Proxy) Conns

func (p *Proxy) Conns() int

Conns is the number of open connections.

func (*Proxy) Fault

func (p *Proxy) Fault() Fault

Fault returns the current fault.

func (*Proxy) SetFault

func (p *Proxy) SetFault(f Fault)

SetFault replaces the current fault. A fault with Reset resets every open connection.

func (*Proxy) Start

func (p *Proxy) Start(ctx context.Context) error

Start listens and serves until ctx ends or Close is called.

type ProxyInfo

type ProxyInfo struct {
	Name     string `json:"name"`
	Listen   string `json:"listen"`
	Upstream string `json:"upstream"`
	Conns    int    `json:"conns"`
	Fault    Fault  `json:"fault"`
}

ProxyInfo describes a proxy.

type Request

type Request struct {
	Kind Kind `json:"kind"`
	// Target is a proxy name, a container name or id, or a deployment as
	// namespace/name.
	Target string `json:"target"`
	// Fault applies to proxies.
	Fault Fault `json:"fault,omitempty"`
	// Action applies to containers: pause, stop or restart.
	Action string `json:"action,omitempty"`
	// Replicas applies to deployments.
	Replicas *int `json:"replicas,omitempty"`
	// Duration is how long the fault lasts; required, at most MaxDuration.
	Duration time.Duration `json:"duration"`
	// Run and Label say who asked, for the audit log.
	Run   string `json:"run,omitempty"`
	Label string `json:"label,omitempty"`
}

Request asks for one fault for a while.

func (Request) MarshalJSON

func (r Request) MarshalJSON() ([]byte, error)

MarshalJSON writes the duration as a string.

func (*Request) UnmarshalJSON

func (r *Request) UnmarshalJSON(b []byte) error

UnmarshalJSON reads the duration as a string.

type Status

type Status struct {
	Proxies     []ProxyInfo `json:"proxies"`
	Active      []Active    `json:"active"`
	Docker      bool        `json:"docker"`
	Kubernetes  bool        `json:"kubernetes"`
	MaxDuration string      `json:"maxDuration"`
}

Status is the agent's state.

Jump to

Keyboard shortcuts

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