fault

package
v1.30.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package fault defines the fault primitives that rules execute when they fire. Each fault implements Apply(ctx); a non-nil error short-circuits the adapter's Action.Before chain and is delivered to the caller in the adapter's native error model.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrConnDrop = errors.New("chaotic: connection drop")

ErrConnDrop is the sentinel returned by ConnDrop's Apply. Each adapter detects this sentinel via errors.Is and substitutes its native connection-drop error (driver.ErrBadConn, status.Unavailable, etc.).

View Source
var ErrDisconnect = errors.New("chaotic: graceful disconnect")

ErrDisconnect is the sentinel returned by Disconnect's Apply. Each adapter detects this sentinel and substitutes its native graceful-close error (an orderly FIN), as distinct from ErrConnDrop's hard reset.

Functions

func ResetClock

func ResetClock(ctx context.Context)

ResetClock stores zero skew into the clock cell bound to ctx, undoing any skew a Clock fault has set (a mid-test reset). A no-op if no cell is bound.

func Skew

func Skew(ctx context.Context) time.Duration

Skew returns the wall-clock skew currently stored in the clock cell bound to ctx, or 0 if no cell is bound. Safe for concurrent use.

func WithClock

func WithClock(ctx context.Context) context.Context

WithClock binds a fresh, zero-skew clock cell to ctx and returns the child context. A Clock fault firing on this context writes its skew into the cell, and engine.Now reads it back. chaos.WithEngine calls this for you; call it directly only when using the engine without the chaos package.

Types

type Fault

type Fault interface {
	Apply(ctx context.Context) error
}

Fault is one chaos primitive. Apply may sleep, return an error, or panic. A return of nil means the fault completed without affecting the call.

func Clock

func Clock(d time.Duration) Fault

Clock returns a fault that, when it fires, sets the skew of the clock cell bound to the context to d, so subsequent engine.Now(ctx) reads observe a wall clock shifted by d. It injects no error and no sleep (Apply returns nil), so it composes with other faults in the same rule. If no clock cell is bound to the context, Apply is a no-op.

Example

ExampleClock shows that a Clock fault, once applied, sets the skew that fault.Skew (and engine.Now) read back from the context.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	ctx := fault.WithClock(context.Background())
	_ = fault.Clock(72 * time.Hour).Apply(ctx)
	fmt.Println(fault.Skew(ctx))
}
Output:
72h0m0s

func ConnDrop

func ConnDrop() Fault

ConnDrop returns ErrConnDrop. Each adapter detects this sentinel and substitutes its native connection-drop error.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// ConnDrop returns a sentinel each adapter maps to its native
	// connection-drop error (driver.ErrBadConn, codes.Unavailable, ...).
	f := fault.ConnDrop()
	err := f.Apply(context.Background())
	fmt.Println(errors.Is(err, fault.ErrConnDrop))
}
Output:
true

func Disconnect

func Disconnect() Fault

Disconnect returns ErrDisconnect, modeling an orderly connection close (a TCP FIN). Distinct from ConnDrop, which models a hard reset; real clients often take different code paths for the two.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// Disconnect returns a sentinel each adapter maps to its native orderly-close
	// error (io.EOF in adapter/net), distinct from ConnDrop's hard reset.
	f := fault.Disconnect()
	err := f.Apply(context.Background())
	fmt.Println(errors.Is(err, fault.ErrDisconnect))
}
Output:
true

func Error

func Error(err error) Fault

Error returns err verbatim from Apply. The adapter is responsible for wrapping it into its native error model (e.g., &url.Error{Op: "chaos"} for http). A nil err makes Apply a no-op.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	f := fault.Error(errors.New("upstream unavailable"))
	fmt.Println(f.Apply(context.Background()))
}
Output:
upstream unavailable

func HTTPStatus

func HTTPStatus(code int, body ...string) Fault

HTTPStatus returns a fault that makes the HTTP adapters render the given status code: adapter/httpsrv writes it, adapter/http synthesizes a response carrying it. Body is optional. When omitted the adapter uses http.StatusText.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// HTTPStatus carries a status code on a sentinel the HTTP adapters render.
	err := fault.HTTPStatus(503, "overloaded").Apply(context.Background())
	var hse *fault.HTTPStatusError
	errors.As(err, &hse)
	fmt.Println(hse.StatusCode(), hse.Body)
}
Output:
503 overloaded

func HeaderInject

func HeaderInject(key, value string) Fault

HeaderInject returns a fault that sets header key to value on the headers flowing toward the code under test.

func HeaderStrip

func HeaderStrip(key string) Fault

HeaderStrip returns a fault that deletes header key from the headers flowing toward the code under test.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// HeaderStrip yields a sentinel describing a header deletion; the adapters
	// apply it to the headers flowing toward the code under test.
	err := fault.HeaderStrip("X-Trace-Id").Apply(context.Background())
	var hf *fault.HeaderFault
	errors.As(err, &hf)
	fmt.Println(hf.Strip, hf.Key)
}
Output:
true X-Trace-Id

func Jittered

func Jittered(lo, hi time.Duration) Fault

Jittered sleeps for a uniformly random duration in [lo, hi]. Negative or zero values are treated as "no sleep". If hi <= lo, sleeps for lo.

func JitteredSeed

func JitteredSeed(lo, hi time.Duration, seed int64) Fault

JitteredSeed is like Jittered but draws from a seeded PCG source, so the sequence of sleep durations is reproducible across runs with the same seed. Use it when a chaos test must be deterministically replayable. The draw is mutex-guarded. The fault is safe for concurrent use.

Example
package main

import (
	"context"
	"time"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// JitteredSeed draws sleep durations from a seeded source, so a chaos test
	// replays the same delays across runs. No Output: the delays are time-based
	// and intentionally not printed.
	f := fault.JitteredSeed(10*time.Millisecond, 50*time.Millisecond, 42)
	_ = f.Apply(context.Background())
}

func Latency

func Latency(d time.Duration) Fault

Latency sleeps for d. Returns ctx.Err() if the context is canceled first. A non-positive d returns immediately.

Example (ContextCancellation)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// A canceled context makes a latency fault return immediately with the
	// context error instead of sleeping.
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	f := fault.Latency(time.Hour)
	fmt.Println(f.Apply(ctx))
}
Output:
context canceled

func Panic

func Panic(v any) Fault

Panic calls panic(v) from Apply. The panic propagates through the action, through the adapter, out to the caller. Recovery is the caller's responsibility.

Example
package main

import (
	"context"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	f := fault.Panic("boom")
	defer func() {
		fmt.Println("recovered:", recover())
	}()
	_ = f.Apply(context.Background())
}
Output:
recovered: boom

func ResponseMutate added in v1.27.0

func ResponseMutate(fn func(any) any) Fault

ResponseMutate runs fn on the wrapped call's result after a SUCCESSFUL call, returning fn's (possibly modified) value to the adapter. It is a no-op in the Before fault chain and never short-circuits other faults in the same rule.

fn receives the adapter's native result as any (*http.Response for the http client, the reply message for a gRPC unary call). fn must return a value of the same concrete type; a mismatched or nil return is ignored by the adapter, which keeps the original result. Prefer the typed helpers where they exist (adapter/http.MutateResponse, adapter/grpc.MutateReply).

func SlowReader

func SlowReader(rate int) Fault

SlowReader rate-limits reads from a chaosio-wrapped reader to rate bytes/sec. rate == 0 blocks until the context is done (models a body that never ends).

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/RomanAgaltsev/chaotic/fault"
)

func main() {
	// Stream-shaping faults return a *StreamFaultError that adapter/io detects
	// via errors.As and uses to shape the stream (here: 1 KB/s).
	err := fault.SlowReader(1024).Apply(context.Background())
	var sfe *fault.StreamFaultError
	fmt.Println(errors.As(err, &sfe), sfe.Rate)
}
Output:
true 1024

func SlowWriter

func SlowWriter(rate int) Fault

SlowWriter is the symmetric write-side rate limit.

func Truncate

func Truncate(n int) Fault

Truncate cuts a chaosio-wrapped stream off after n bytes: a reader returns io.EOF past n, a writer returns io.ErrShortWrite past n.

type HTTPStatusError

type HTTPStatusError struct {
	Code int
	Body string // empty => the adapter substitutes http.StatusText(Code)
}

HTTPStatusError is the sentinel an HTTPStatus fault returns. The HTTP adapters detect it via errors.As and render the status instead of the generic 500. The fault package stays free of net/http: this carries only the code and an optional body string.

func (*HTTPStatusError) Error

func (e *HTTPStatusError) Error() string

func (*HTTPStatusError) StatusCode

func (e *HTTPStatusError) StatusCode() int

StatusCode reports the HTTP status code the fault should render.

type HeaderFault

type HeaderFault struct {
	Strip bool // true => delete Key, false => set Key to Value
	Key   string
	Value string
}

HeaderFault is the sentinel a header fault returns. Adapters detect it via errors.As, apply the mutation to the headers flowing toward the code under test (request headers on the server, response headers on the client), then let the wrapped call proceed - it does NOT abort the call.

Because the engine fires at most one rule per Op and rule's fault chain short-circuits on the first sentinel, only one header fault applies per rule (a preceding Latency is fine, it returns nil and continues). To mutate several headers, use several rules, or wait for a future batch helper.

func (*HeaderFault) Error

func (*HeaderFault) Error() string

type Kind

type Kind int

Kind classifies a Fault for introspection (linting, observability). It does not affect behavior.

const (
	KindUnknown Kind = iota
	KindLatency
	KindJittered
	KindError
	KindPanic
	KindConnDrop
	KindHTTPStatus
	KindHeader
	KindClock
	KindDisconnect
	KindSlowReader
	KindSlowWriter
	KindTruncate
	KindResponseMutate
)

Fault kinds, one per built-in fault type.

func KindOf

func KindOf(f Fault) Kind

KindOf returns f's Kind, or KindUnknown for a custom fault that does not implement Kinded.

type Kinded

type Kinded interface {
	Kind() Kind
}

Kinded is implemented by every built-in fault so tools can classify it without type-switching on unexported types.

type StreamFaultError

type StreamFaultError struct {
	Mode  StreamMode
	Rate  int // bytes/sec for the slow modes; 0 means "block until ctx" (never-ends)
	Limit int // byte cap for StreamTruncate
}

StreamFaultError is the sentinel the stream-shaping faults return. adapter/io detects it via errors.As and shapes the data stream instead of aborting the call. The fault package stays free of io-wrapping code: this carries only the mode and its parameters.

func (*StreamFaultError) Error

func (e *StreamFaultError) Error() string

type StreamMode

type StreamMode int

StreamMode discriminates the three stream-shaping faults inside a StreamFaultError.

const (
	StreamSlowRead StreamMode = iota
	StreamSlowWrite
	StreamTruncate
)

Stream-shaping modes.

Jump to

Keyboard shortcuts

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