github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/healthz/indicator.go (about)

     1  package healthz
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    10  )
    11  
    12  // Indicator missing godoc
    13  //go:generate mockery --name=Indicator --output=automock --outpkg=automock --case=underscore --disable-version-string
    14  type Indicator interface {
    15  	Name() string
    16  	Configure(IndicatorConfig)
    17  	Run(ctx context.Context)
    18  	Status() Status
    19  }
    20  
    21  // Status missing godoc
    22  //go:generate mockery --name=Status --output=automock --outpkg=automock --case=underscore --disable-version-string
    23  type Status interface {
    24  	Error() error
    25  	Details() string
    26  }
    27  
    28  // IndicatorFunc missing godoc
    29  type IndicatorFunc func(ctx context.Context) Status
    30  
    31  // Implements Status interface
    32  type status struct {
    33  	error   error
    34  	details string
    35  }
    36  
    37  // Error returns status error
    38  func (s *status) Error() error {
    39  	return s.error
    40  }
    41  
    42  // Details returns status details
    43  func (s *status) Details() string {
    44  	return s.details
    45  }
    46  
    47  // Implements Indicator interface
    48  type indicator struct {
    49  	name string
    50  
    51  	interval     time.Duration
    52  	timeout      time.Duration
    53  	initialDelay time.Duration
    54  	threshold    int
    55  
    56  	indicatorFunc IndicatorFunc
    57  	status        Status
    58  	statusLock    sync.Mutex
    59  	failureCount  int
    60  }
    61  
    62  // NewIndicator returns new indicator with the provided name and IndicatorFunc
    63  func NewIndicator(name string, indicatorFunc IndicatorFunc) Indicator {
    64  	return &indicator{
    65  		name:          name,
    66  		indicatorFunc: indicatorFunc,
    67  		status:        &status{details: ""},
    68  		statusLock:    sync.Mutex{},
    69  		failureCount:  0,
    70  	}
    71  }
    72  
    73  // Name returns indicator name
    74  func (i *indicator) Name() string {
    75  	return i.name
    76  }
    77  
    78  // Configure sets indicator config based on IndicatorConfig
    79  func (i *indicator) Configure(cfg IndicatorConfig) {
    80  	i.interval = cfg.Interval
    81  	i.timeout = cfg.Timeout
    82  	i.initialDelay = cfg.InitialDelay
    83  	i.threshold = cfg.Threshold
    84  }
    85  
    86  // Run starts the periodic indicator checks
    87  func (i *indicator) Run(ctx context.Context) {
    88  	go func() {
    89  		<-time.After(i.initialDelay)
    90  
    91  		ticker := time.NewTicker(i.interval)
    92  		for {
    93  			timeoutCtx, cancel := context.WithTimeout(ctx, i.timeout)
    94  			currentStatus := i.indicatorFunc(timeoutCtx)
    95  			cancel()
    96  
    97  			i.statusLock.Lock()
    98  			if currentStatus.Error() != nil {
    99  				// escape overflow
   100  				if i.failureCount < math.MaxInt32 {
   101  					i.failureCount++
   102  				}
   103  				log.C(ctx).Warnf("Threshold for indicator %s is %d, current failure count is : %d, current error is: %s, details are: %s",
   104  					i.Name(),
   105  					i.threshold,
   106  					i.failureCount,
   107  					currentStatus.Error(),
   108  					currentStatus.Details())
   109  			} else {
   110  				i.failureCount = 0
   111  			}
   112  
   113  			if (i.failureCount > i.threshold || i.failureCount == 0) && i.status.Error() != currentStatus.Error() {
   114  				log.C(ctx).Infof("Changing indicator %s state to %+v", i.Name(), currentStatus)
   115  				i.status = currentStatus
   116  			}
   117  			i.statusLock.Unlock()
   118  
   119  			select {
   120  			case <-ctx.Done():
   121  				ticker.Stop()
   122  				return
   123  			case <-ticker.C:
   124  			}
   125  		}
   126  	}()
   127  }
   128  
   129  // Status reports the last calculated status of the indicator
   130  func (i *indicator) Status() Status {
   131  	i.statusLock.Lock()
   132  	defer i.statusLock.Unlock()
   133  	return i.status
   134  }