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 ¶
- Variables
- func ResetClock(ctx context.Context)
- func Skew(ctx context.Context) time.Duration
- func WithClock(ctx context.Context) context.Context
- type Fault
- func Clock(d time.Duration) Fault
- func ConnDrop() Fault
- func Disconnect() Fault
- func Error(err error) Fault
- func HTTPStatus(code int, body ...string) Fault
- func HeaderInject(key, value string) Fault
- func HeaderStrip(key string) Fault
- func Jittered(min, max time.Duration) Fault
- func JitteredSeed(min, max time.Duration, seed int64) Fault
- func Latency(d time.Duration) Fault
- func Panic(v any) Fault
- func ResponseMutate(fn func(any) any) Fault
- func SlowReader(rate int) Fault
- func SlowWriter(rate int) Fault
- func Truncate(n int) Fault
- type HTTPStatusError
- type HeaderFault
- type Kind
- type Kinded
- type StreamFaultError
- type StreamMode
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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.).
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
HeaderInject returns a fault that sets header key to value on the headers flowing toward the code under test.
func HeaderStrip ¶
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 ¶
Jittered sleeps for a uniformly random duration in [min, max]. Negative or zero values are treated as "no sleep". If max <= min, sleeps for min.
func JitteredSeed ¶
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())
}
Output:
func Latency ¶
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 ¶
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
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 ¶
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 ¶
SlowWriter is the symmetric write-side rate limit.
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.
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.