123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- /*
- Copyright 2015 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // Package chaosclient makes it easy to simulate network latency, misbehaving
- // servers, and random errors from servers. It is intended to stress test components
- // under failure conditions and expose weaknesses in the error handling logic
- // of the codebase.
- package chaosclient
- import (
- "errors"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "reflect"
- "runtime"
- "k8s.io/kubernetes/pkg/util/net"
- )
- // chaosrt provides the ability to perform simulations of HTTP client failures
- // under the Golang http.Transport interface.
- type chaosrt struct {
- rt http.RoundTripper
- notify ChaosNotifier
- c []Chaos
- }
- // Chaos intercepts requests to a remote HTTP endpoint and can inject arbitrary
- // failures.
- type Chaos interface {
- // Intercept should return true if the normal flow should be skipped, and the
- // return response and error used instead. Modifications to the request will
- // be ignored, but may be used to make decisions about types of failures.
- Intercept(req *http.Request) (bool, *http.Response, error)
- }
- // ChaosNotifier notifies another component that the ChaosRoundTripper has simulated
- // a failure.
- type ChaosNotifier interface {
- // OnChaos is invoked when a chaotic outcome was triggered. fn is the
- // source of Chaos and req was the outgoing request
- OnChaos(req *http.Request, c Chaos)
- }
- // ChaosFunc takes an http.Request and decides whether to alter the response. It
- // returns true if it wishes to mutate the response, with a http.Response or
- // error.
- type ChaosFunc func(req *http.Request) (bool, *http.Response, error)
- func (fn ChaosFunc) Intercept(req *http.Request) (bool, *http.Response, error) {
- return fn.Intercept(req)
- }
- func (fn ChaosFunc) String() string {
- return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
- }
- // NewChaosRoundTripper creates an http.RoundTripper that will intercept requests
- // based on the provided Chaos functions. The notifier is invoked when a Chaos
- // Intercept is fired.
- func NewChaosRoundTripper(rt http.RoundTripper, notify ChaosNotifier, c ...Chaos) http.RoundTripper {
- return &chaosrt{rt, notify, c}
- }
- // RoundTrip gives each ChaosFunc an opportunity to intercept the request. The first
- // interceptor wins.
- func (rt *chaosrt) RoundTrip(req *http.Request) (*http.Response, error) {
- for _, c := range rt.c {
- if intercept, resp, err := c.Intercept(req); intercept {
- rt.notify.OnChaos(req, c)
- return resp, err
- }
- }
- return rt.rt.RoundTrip(req)
- }
- var _ = net.RoundTripperWrapper(&chaosrt{})
- func (rt *chaosrt) WrappedRoundTripper() http.RoundTripper {
- return rt.rt
- }
- // Seed represents a consistent stream of chaos.
- type Seed struct {
- *rand.Rand
- }
- // NewSeed creates an object that assists in generating random chaotic events
- // based on a deterministic seed.
- func NewSeed(seed int64) Seed {
- return Seed{rand.New(rand.NewSource(seed))}
- }
- type pIntercept struct {
- Chaos
- s Seed
- p float64
- }
- // P returns a ChaosFunc that fires with a probability of p (p between 0.0
- // and 1.0 with 0.0 meaning never and 1.0 meaning always).
- func (s Seed) P(p float64, c Chaos) Chaos {
- return pIntercept{c, s, p}
- }
- // Intercept intercepts requests with the provided probability p.
- func (c pIntercept) Intercept(req *http.Request) (bool, *http.Response, error) {
- if c.s.Float64() < c.p {
- return c.Chaos.Intercept(req)
- }
- return false, nil, nil
- }
- func (c pIntercept) String() string {
- return fmt.Sprintf("P{%f %s}", c.p, c.Chaos)
- }
- // ErrSimulatedConnectionResetByPeer emulates the golang net error when a connection
- // is reset by a peer.
- // TODO: make this more accurate
- // TODO: add other error types
- // TODO: add a helper for returning multiple errors randomly.
- var ErrSimulatedConnectionResetByPeer = Error{errors.New("connection reset by peer")}
- // Error returns the nested error when C() is invoked.
- type Error struct {
- error
- }
- // C returns the nested error
- func (e Error) Intercept(_ *http.Request) (bool, *http.Response, error) {
- return true, nil, e.error
- }
- // LogChaos is the default ChaosNotifier and writes a message to the Golang log.
- var LogChaos = ChaosNotifier(logChaos{})
- type logChaos struct{}
- func (logChaos) OnChaos(req *http.Request, c Chaos) {
- log.Printf("Triggered chaotic behavior for %s %s: %v", req.Method, req.URL.String(), c)
- }
|