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  }