github.com/hashicorp/vault/sdk@v0.13.0/physical/error.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package physical 5 6 import ( 7 "context" 8 "errors" 9 "math/rand" 10 "sync" 11 "time" 12 13 log "github.com/hashicorp/go-hclog" 14 ) 15 16 const ( 17 // DefaultErrorPercent is used to determin how often we error 18 DefaultErrorPercent = 20 19 ) 20 21 // ErrorInjector is used to add errors into underlying physical requests 22 type ErrorInjector struct { 23 backend Backend 24 errorPercent int 25 randomLock *sync.Mutex 26 random *rand.Rand 27 } 28 29 // TransactionalErrorInjector is the transactional version of the error 30 // injector 31 type TransactionalErrorInjector struct { 32 *ErrorInjector 33 Transactional 34 } 35 36 // Verify ErrorInjector satisfies the correct interfaces 37 var ( 38 _ Backend = (*ErrorInjector)(nil) 39 _ Transactional = (*TransactionalErrorInjector)(nil) 40 ) 41 42 // NewErrorInjector returns a wrapped physical backend to inject error 43 func NewErrorInjector(b Backend, errorPercent int, logger log.Logger) *ErrorInjector { 44 if errorPercent < 0 || errorPercent > 100 { 45 errorPercent = DefaultErrorPercent 46 } 47 logger.Info("creating error injector") 48 49 return &ErrorInjector{ 50 backend: b, 51 errorPercent: errorPercent, 52 randomLock: new(sync.Mutex), 53 random: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))), 54 } 55 } 56 57 // NewTransactionalErrorInjector creates a new transactional ErrorInjector 58 func NewTransactionalErrorInjector(b Backend, errorPercent int, logger log.Logger) *TransactionalErrorInjector { 59 return &TransactionalErrorInjector{ 60 ErrorInjector: NewErrorInjector(b, errorPercent, logger), 61 Transactional: b.(Transactional), 62 } 63 } 64 65 func (e *ErrorInjector) SetErrorPercentage(p int) { 66 e.errorPercent = p 67 } 68 69 func (e *ErrorInjector) addError() error { 70 e.randomLock.Lock() 71 roll := e.random.Intn(100) 72 e.randomLock.Unlock() 73 if roll < e.errorPercent { 74 return errors.New("random error") 75 } 76 77 return nil 78 } 79 80 func (e *ErrorInjector) Put(ctx context.Context, entry *Entry) error { 81 if err := e.addError(); err != nil { 82 return err 83 } 84 return e.backend.Put(ctx, entry) 85 } 86 87 func (e *ErrorInjector) Get(ctx context.Context, key string) (*Entry, error) { 88 if err := e.addError(); err != nil { 89 return nil, err 90 } 91 return e.backend.Get(ctx, key) 92 } 93 94 func (e *ErrorInjector) Delete(ctx context.Context, key string) error { 95 if err := e.addError(); err != nil { 96 return err 97 } 98 return e.backend.Delete(ctx, key) 99 } 100 101 func (e *ErrorInjector) List(ctx context.Context, prefix string) ([]string, error) { 102 if err := e.addError(); err != nil { 103 return nil, err 104 } 105 return e.backend.List(ctx, prefix) 106 } 107 108 func (e *TransactionalErrorInjector) Transaction(ctx context.Context, txns []*TxnEntry) error { 109 if err := e.addError(); err != nil { 110 return err 111 } 112 return e.Transactional.Transaction(ctx, txns) 113 } 114 115 // TransactionLimits implements physical.TransactionalLimits 116 func (e *TransactionalErrorInjector) TransactionLimits() (int, int) { 117 if tl, ok := e.Transactional.(TransactionalLimits); ok { 118 return tl.TransactionLimits() 119 } 120 // We don't have any specific limits of our own so return zeros to signal that 121 // the caller should use whatever reasonable defaults it would if it used a 122 // non-TransactionalLimits backend. 123 return 0, 0 124 }